Changelog
Was ist neu?
Jede Verbesserung an der Plattform, wie sie geliefert wird.
· kamo-shared-library
Bill die ganze Begriff im Voraus, und sagen Sie so auf das Zitat
Eine Org, die Gebühren im Voraus sah die monatliche Rate auf einem Drei-Jahres-Vertrag. Die Einstellung zu entscheiden, dass bereits existierte - ************ aber erreichte nur QuoteConversionService, zum Zeitpunkt der Umstellung. Die Das Dokument selbst wurde immer so bewertet, als ob der Kunde pro Zeitraum bezahlt hätte. AnwendungOptionHeadlineTotals verzweigt sich jetzt auf der Basis: FULL_CONTRACT nimmt contractTotal (die bereits trägt die Setup-Gebühr, so darf es nicht hinzugefügt werden zweimal), FIRST_PERIOD_PLUS_SETUP behält die erste Rechnung. periodTotal und monatlich Äquivalent sind unter beiden unberührt - die Sparplaketten vergleichen monatlichÄquivalent, und bewegen würde es jeden Maßstab auf der Seite ändern. Die Grundlage ist auf das Zitat gesetzt, wenn es live angehoben und nicht aufgelöst wird. Ein Zitat gibt Bedingungen, und ein Administrator Flip der Org-Schalter nächste Woche muss Sie nicht auf einem Dokument, das ein Kunde bereits besitzt, neu zu bekräftigen. Lesen der Org-Einstellung bei der Umstellung Zeit tat genau das: es neu berechnet Zitate, die bereits waren angenommen. Konvertierung liest jetzt die eigene Briefmarke des Zitats, und überarbeiten trägt es über so eine Revision kann nicht stillschweigend eine Live-Verhandlung neu zu inszenieren. Die Spalte ist nichtig, weil jede vorhandene Zeile vor ihr; null löst sich auf FIRST_PERIOD_PLUS_SETUP, das ist, was diese Zitate gesendet wurden, so nichts bereits in den Händen eines Kunden wechselt den Wert.
· DaemonService
Tick SecurityService's planmäßiger Bericht Sweep
Alle fünf Minuten. Ein Zeitplan ist auf die Minute eingestellt, so dass eine feinere Zecke nichts kauft ein Mitglied würde bemerken und ein gröseres macht einen 8:00 Bericht um 8:15 Uhr ankommen. DaemonService besitzt dies aus dem gleichen Grund, aus dem es das Legal-Paket besitzt und Lead-E-Mail Sweeps: Repliken: 1 mit Strategie Rek erstellen bedeutet, dass die alte Hülse weg ist bevor das neue beginnt, so dass ein Einsatzfenster nicht doppelt feuern kann. A @Scheduled in SecurityService, die mit Überspannung rollt, kann nachweislich und es gibt keine ShedLock überall in dieser Flotte, so dass die pod zählen ist die ganze Garantie. @DisallowConcurrentExecution, weil ein langer Sweep darf nicht eine zweite haben Anfang obendauf: Beide würden die gleichen Zeitpläne fällig finden, da nextRunAt ist nur fortgeschritten, wenn die erste sie beansprucht hat, und der gleiche Bericht würde gehen zweimal. Explizite Timeouts aus dem gleichen Grund die Geschwister-Jobs haben sie - ein Zahlungsausfall RestTemplate wartet ewig, und ein gepinntes Faden bedeutet hier, dass der Sweep nie läuft wieder, bis die Hülse neu startet, was nicht von "nichts fällig" zu unterscheiden ist. Nichts an einem Bericht kreuzt diesen Prozess: die Sweep-Rücksendungen zählen, nie rudert.
· SecurityService
Führen Sie die Berichte, die fällig sind, und senden Sie sie
Ein interner Endpunkt, X-Internal-Auth gated und konstant-zeit verglichen, dass DaemonService tickt. Der Cron bleibt im Daemon, weil es Repliken ist: 1 mit Strategie Rek erstellen, so dass ein Einsatzfenster nicht doppelt feuern kann; ein @Scheduled in einem Dienst die Rollen mit Überspannung nachweislich kann, und es gibt keine ShedLock überall in dieser Flotte. Für einen Job, dessen Ausgang die Zahlen einer Organisation sind, die an eine Liste von Adressen gemailt wird, "üblich einmal" ist keine Eigenschaft. Jeder Zeitplan läuft als seine OWNER, mit Rechten zur Laufzeit von ihrem gespeichert Zuschüsse - nicht als Daemon, die keine hat, und nicht als wer zuletzt bearbeitet der Zeitplan. Also widerrufen jemanden den Zugriff stoppt ihre Zeitpläne produzieren Daten sie sollten nicht mehr bei der nächsten laufen sehen, anstatt wann immer eine Sitzung abläuft. Ohne dies ist ein Zeitplan eine Möglichkeit, den Server lesen Daten auf Ihrem für das, was Sie nicht selbst lesen konnten. nextRunAt ist fortgeschritten VOR Lieferung wird versucht. Ein Crash Mitte der Lieferung dann Kosten ein verpasster Senden anstatt einer grenzenlosen Schleife von Duplikaten, und eine verpasste wöchentliche Bericht ist ein viel billigerer Fehler als der gleiche Bericht Ankunft vierzig Mal. Die gerenderte Datei geht als Anhang und nicht als Link, weil diese routinemäßig an einen Kunden oder einen Buchhalter ohne Konto geschickt. Eine schlechte Adresse tut nicht kosten die anderen Empfänger ihren Bericht, aber ein Lauf, wo jede Adresse fehlgeschlagen ist aufgezeichnet FAILED anstatt geliefert-zu-niemand. Ein leerer Bericht wird nicht gesendet, es sei denn der Besitzer fragte danach: Eine wöchentliche E-Mail, die immer ankommt, hört auf zu lesen.
· EmailService
Lassen Sie eine transaktionale senden Sie einen Anhang tragen
Zusätzliche für geplante Berichte, die eine gerenderte PDF oder Arbeitsbuch, die hat erreichen Menschen, die kein Konto haben - ein Link wäre keine Lieferung für sie. Die SMTP-Schicht hat Anhänge genommen, seit sie geschrieben wurde; dieser Pfad ist einfach vergangen null. Also nichts darüber, wie Mail gebaut wird Änderungen, das Feld ist optional, und jeder Der vorhandene Anrufer nimmt genau die Route, die er immer gemacht hat. Base64 statt mehrteilig, weil dieser Endpunkt JSON Server-zu-Server ist und Jeder Anrufer baut bereits einen JSON-Körper. Überdimensionierte und missgebildete Anbaugeräte REFUSED statt gekürzt: eine prallte 40MB Arbeitsbuchbörse kostet die sendende Domain Ruf, und eine still abgeschnittene ist eine Datei, die der Empfänger nicht öffnen kann und kann nicht erklären. Der Wurf ist, was lässt den Anrufer aufnehmen einen gescheiterten Lauf statt einer geliefert ein.
· kamo-shared-library
Die E-Mail ein geplanter Bericht kommt an
REPORT_SCHEDULED, registriert in CANONICAL_KEYS - abwesend in dieser Liste eine Vorlage ist eine permanente 404, was auch immer Dateien neben ihm existieren. Das Gremium gibt den Datenumfang an. Ein Empfänger, der nicht der Zeitplan ist, hat keine andere Möglichkeit, eine ganze Organisationssumme von einem zu sagen, der nur das abdeckt, was der Eigentümer kann sehen, und das sind verschiedene Dokumente. Es benennt auch den Besitzer zweimal: einmal als wer den Zeitplan festgelegt hat, und einmal, als wessen Zahlen diese sind.
· SecurityService
Die Listen eine Aktiendialog wählt aus, und ein Aktienziel, das jede ID passt
GET ************ kehrt die Leute der Organisation zurück, Abteilungen und Rollen als Picker-Optionen, in einem Anruf und in einer Form. Auf gebaut die Meldeoptionsanbieter und nicht auf den Mitgliedern, Abteilungen und Rollen Endpunkte: Jene beantworten die Frage eines anderen Bildschirms und geben eine andere zurück Form, und eine gemeinsame Dialog verbrauchen alle drei würde brechen, wenn einer von ihnen aus Gründen, die nichts mit der Berichterstattung zu tun haben, geändert. Gated on SHARE_REPORTS . Jeder in der Organisation ist nicht etwas, um ein Mitglied ohne Grund zu übergeben um es zu sehen. Ein Aktienziel ist jetzt durchgehend ein String. Ein Mitglieds-ID ist ein 19-stelliger Long und ein Abteilung oder Rollen-ID ist eine UUID; die Lange Spalte, mit der diese begonnen hat, könnte die Hälfte halten die Arten, und die Hälfte es konnte nicht durch stille übereinstimmen gescheitert Niemand. Die Abteilung und Rollenprüfungen vergleichen als Text und Auflösung durch Mitgliedschaft JETZT und nicht durch das, was eine Sitzung geschah, um zu halten, wenn die Aktie gemacht wurde. Jeder der drei Mitgliedschaftschecks scheitert geschlossen. Eine Aktie, die nicht sein kann bewertet nicht gewährt.
· kamo-internal
Schritt durch jede Suche Hit, nicht nur die neuesten
Die Suche am oberen Rand eines Chat-Fensters markierte jedes Auftreten der Abfrage und gescrollt, um genau einer von ihnen - die neueste - mit nichts zu sagen, wie viele andere es gab und keine Möglichkeit, sie zu erreichen. Ein Mitglied sucht ein Gespräch für eine Rechnung Nummer konnte das Wort sehen, das drei Bildschirme oben hervorgehoben und hatte keine Kontrolle, die würde bringen Sie sie zu ihm. Es verhält sich jetzt, wie die Fundstange es bereits ähnelt: ein Zähler mit der Aufschrift "3/12", ein Pfeil zu den älteren Nachrichten und eine in Richtung der neueren, Enter und Shift+Enter für die gleichen zwei Züge, und der Treffer stand auf in massiven Bernstein gegen das Gelb der Ruhe. Die Landung auf dem neuesten Hit ist unverändert, so dass ein Mitglied, das immer nur eine Abfrage getippt hat und lesen das Ergebnis sieht, was sie immer getan haben, jetzt mit der Zählung daneben. Zwei Entscheidungen darunter sind es wert, buchstabiert zu werden. Hits werden pro Vorkommen und nicht pro Nachricht gezählt, und gegen die Wörter Blasen tatsächlich zeichnen - die Übersetzung, wo eine Nachricht hat eine, die die alte Suche hat nicht getan. Der Zähler und die Markierungen sind zwei Ansichten einer Liste, die von einer einzelnen erstellt werden scan (findOccurrences, gemeinsam mit dem Highlighter), so "12" ist immer genau die Anzahl der Markierungen auf dem Bildschirm. Zwei Scans, die auch nur leicht widersprach würde Pfeile verlassen Landung auf nichts. Seited: Nachrichten gelangen unten an, während eine Suche geöffnet ist und ältere Seiten bei der als "Ergebnis 3" würde ruhig eine andere Botschaft werden. Daraus folgt, dass "1 von 3" bei der ältester Hit bedeutet nicht, dass es nichts Älteres gibt - nur, dass nichts Älteres gewesen ist geholt - also zurücktreten von dort fragt nach der vorherigen Seite und landet auf dem, was es bringt die gleiche Bewegung, die die Liste bereits macht, wenn jemand nach oben scrollt. Es wickelt zum neuesten, wenn der Server ausgelaufen ist, oder nach fünf Seiten eingetroffen sind keine Treffer tragen: Ein Druck eines Pfeils darf nicht in der Lage sein, ein Jahre lang zu gehen Konverkuieren zurück zu seiner ersten Botschaft. Die ruhende Geometrie der Werkzeugleiste ist unberührt. Der Zähler und die Pfeile erscheinen mit der Abfrage statt mit dem Feld, weil sie etwa sechzig Pixel und eine leere Kosten Suchbox hat nichts zu zählen.
· kamo-shared-library
Ein Service-Arbeitsreport und ein Share-Ziel, das zu jeder ID passt
ServiceWorkReport ist der zehnte Saatgutbericht und der, der den Umfang auf Zeilenebene ausmacht real und nicht nur anwesend. Commerce trägt das eigene/alles Paar der Plattform VIEW_OWN_SERVICE_WORK sitzt neben VIEW_SERVICE_WORK als Geschwister und einen Besuch trägt zugewiesenMemberId, so dass ein Techniker hält nur die engere Rechte sieht ihre eigene Arbeit, während ihr Vorgesetzter die Filiale sieht. Eine Definition, zwei Antworten, und nichts für die Berichterstattung erfunden. Ohne sie wären SELF und TEAM ungetestete Pfade in dem Teil dieses Systems ist es am wenigsten akzeptabel zu gehen ungetestet. Reihen sind Besuche, keine Jobs: Ein Job mit drei Besuchen ist drei Arbeiten, möglicherweise von drei Personen an drei Tagen, und sie zusammenbrechen macht "wer was getan hat, wann" unbeantwortbar. ReportViewShare.targetId wird target_key, ein String. Eine Mitglieds-ID ist eine 19-stellige Lange aber eine Abteilung und eine Rolle sind UUIDs, so dass eine INT8-Spalte die Hälfte halten könnte Aktien-Arten - und die Hälfte hätte es nicht versäumt, still zu passen Niemand. Umbenennt statt neu geschrieben, weil Hibernate Schema-Update fügt eine die Spalte und nicht neu eingeben; die alte Spalte wurde nie geschrieben. LeadPipelineConversionReport wendet nun das eigene, nicht zugeordnete Boden des Lead Grid an. Ohne sie ist der Bericht eine zweite, ungeprüfte Möglichkeit, den nicht zugewiesenen Pool zu sehen, und seine Gruppenzählungen sind ohne sichtbaren Grund nicht einverstanden mit /leads.
· kamo-internal
Pin die bekannten Ordner an die Spitze der Sidebar
Posteingang, Entwürfe, Sent, Junk, Trash und Archive fahren nicht mehr. Sie halten diese Ordnung an der Spitze der Ordnerliste, und alles, was das Mitglied gemacht sitzt darunter unter einer "Meine Ordner" Überschrift, noch frei nachbestellbar. Die angehefteten Zeilen sind gar nicht mit dnd-kit registriert, anstatt die Drop: eine Zeile, die nur Decoder zu bewegen ist immer noch ein Drop-Ziel, so fallen ein Ordner auf den Posteingang würde die gesamte Liste animieren und dann still legen Sie es zurück. Sie halten die Griffsäule als leeren Raum, so dass beide Gruppen eine linke teilen Kante statt als zwei Listen zu lesen. Pinning ist bewusst schmaler als isSystemFolder, die das Blatt bei jede Tiefe, so dass die eigenen Clients eines Mitglieds.Archive zeichnet das Archiv-Symbol. Das ist die richtiger Aufruf für ein Symbol und das falsche für eine Position, so pinnedRank schaut auf Wurzeln nur - ein Ordner jemand unter ihren eigenen Eltern eingereicht bleibt ihre die Unterordner eines gepinnten Ordners, ebenso wie die Unterordner eines gepinnten Ordners. Spam sitzt in Junks Schlitz. Das Backend normalisiert sich zu Junk, wo immer es schreibt, so ein Server-Auflistung "Spam" nennt den gleichen Ordner und sollte nicht unter die des Mitglieds. Der Rang lebt in OrdnerTargets, der bereits den freigegebenen Ordner besitzt Wortschatz, so dass der Umzug Picker seine Wurzeln auf die gleiche Weise und die beiden Oberflächen können nicht wieder auseinander driften.Ordner bewegen sich auf lib/email neben it: vitest's gehören globs nicht abdecken App / Hooks, so die Reihenfolge Logik dort gelassen wurde von nichts eingesammelt und per Telefon ohne Laufen passiert. Mitglieder, die ihren Posteingang vor dieser Änderung gezogen haben, benennen ihn noch in ihrem gespeicherten Präferenz. Diese Einträge werden ignoriert, und der nächste Drag schreibt sie heraus.
· kamo-marketing
Bauen Sie den Index als Karte des Produkts wieder auf
Es war eine rekursive Liste aller dreihundert Artikel, eingerückt durch Tiefe. Alles war auf der Seite und nichts war auffindbar, und die Seite gewogen 3.9 MB. Die Form ist dem Produkt entlehnt. Die Haustür eines KamoCRM-Kunden ist die Home Launchpad -- ein Raster von Bereichen, die Sie wählen aus -- so die Hilfe-Center-Front Tür ist die gleiche Geste, und jemand, der schon ankommt, weiß, wie man sie liest. Die Kacheln sind eine maßstabsgetreue Karte und kein Kartenraster. Jeder listet auf, was eigentlich ist in ihm mit realen Zählern, so dass eine Kachel ist so hoch wie seine Fläche ist groß: Client Relations Manager hat acht Abschnitte und neunundvierzig Artikel und sieht es, nächste Kalender mit fünf. Equal-Höhe-Karten mit einem Symbol pro Stück wäre aufgeräumter und würde das Produkt falsch darstellen -- und sechzehn Umriss glyphen differenzieren Sechzehn Bereiche weitaus schlechter als sechzehn Inhaltsverzeichnisse. Bereiche, die keine haben Abschnitte listen ihre Artikel stattdessen, so dass keine Kachel sagt nur ihren eigenen Namen. Suche ist die primäre Aktion und sitzt jetzt über alles, rangiert (ein Hit bei der Start eines Titels schlägt einen später, der einen im Körper schlägt) und gruppiert nach Fläche, weil "welcher Teil des Produkts ist dies in" ist die meisten der Antwort. Spiele sind an Ort und Stelle markiert. Ein Startpunktstreifen für einen Erstbesucher, absichtlich unnummeriert: drei lesen sich als Sequenz und sind keine, da die dritte ist ein Administrator Arbeit, die die meisten Mitglieder nie tun werden. Grafen und Augenbrauen sind in der Mono-Gesicht bereits in der Marke gesetzt, und die beige sekundär -- die die Website fast aufgehört hatte mit der Struktur. Das hält die Seite von monochromen grün, ohne die Einführung alles, was nicht schon KamoCRM ist. Auch die Kategorie- und Abschnittsseiten enden nicht mehr in Sackgassen: "In diesem Abschnitt" macht die Links, die der Server nie gesendet hat, zwei-up mit Auszügen und einem zählen, wo ein Abschnitt einen hat.
· kamo-internal
Das Report Studio - eine Bibliothek und ein Renderer für jeden Bericht
/reports ist ein Regal, kein Kartengitter: zweihundert identische abgerundete Boxen sind ein Wand zu scannen, und eine Zeile gibt einen Bericht Namen Raum zu lesen. Gespeicherte Ansichten Nest nach dem Bericht gehören sie, das ist, was lehrt die Unterscheidung die ganze subsystem ruht auf - es gibt keine "Neue Bericht"-Kontrolle überall, weil ein Bericht ist eine Java-Klasse und was ein Mitglied hier speichert, ist eine Konfiguration von einer. Das Studio besteht aus drei Bands: was ihr fragt, ein Satz sagt, was los ist Bildschirm, und die Antwort. Das mittlere Band ist tragend. Ein Bericht Bildschirm versagt wenn jemand Zahlen sehen kann und nicht sagen kann, was sie Zahlen sind, und jeder Frage in dieser Kategorie - wie viele Zeilen, über welchen Zeitraum, verengt wie und zu decken, wie viel von der Organisation passt in einen Satz. Der Data-Skop-Hinweis sitzt dort wörtlich, weil eine Summe produziert unter "Ihre eigenen Aufzeichnungen nur" ist ein unterschiedliches Dokument und nichts anderes auf dem Bildschirm unterscheidet sie. Nichts in App/Komponenten/Berichten fragt, welcher Bericht es darstellt. visibleStudioControls liest die deklarierten Fähigkeiten einer Definition und zeichnet die Passende Steuerungen; ParameterControl-Dispatch auf den Typ eines Parameters; jede Zelle gerendert durch den ReportFormat-Deskriptor seiner Kolumne. Ein KPI-only-Bericht bekommt ein Zeitraum und ein Ausführen-Button und nichts anderes, ohne eine Zeile Code, die es kennt existiert und der Tag, an dem dies aufhört, wahr zu sein, fügt ein Bericht auf, ein Java zu sein Veränderung. formatters.ts ist der eine Ort, an dem ein Wert zu einem String wird, und sein Java-Zwilling liest der gleiche Deskriptor, also eine Figur auf dem Bildschirm und die Figur in einem heruntergeladenen PDF kann nicht widersprechen. Daten gehen eher durch app/lib/serverTime.ts als eine Sekunde parser: Java schreibt LocalDateTime ohne Offset und ein Browser liest, dass als Ortszeit, die Stunden aus ist. Die Chart-Palette ist fixe Hex, nicht abgeleitet von --color-primary. Jeder andere Farbe ist hier ein Farbton der Marke der Organisation, die für Chrom und falsch für ein Diagramm - eine blasse White-Label-Primär würde eine Serie Palette niemand geben könnte auseinander zu halten. Der Auftrag wurde durchsucht und validiert, anstatt beäugt: alle acht passieren das Leichtigkeit Band, Chroma Boden, CVD Trennung, Normal-Vision Boden- und Kontrastkontrollen auf der Lichtfläche dieser App, die die einzige Oberfläche ist hat es. --report-* folgt genau dem --legalen-* und --Training-*-Muster, und sein Siegel ist --accent-work, die die Instrumentenpanel-Palette bereits als Akzent für "Aufgaben, Kalender, Berichte". Berichte nehmen seinen Platz im niv, dem AppType-Spiegel, den Organisationsflaggen und der Berechtigungsbaum. Seine fünf Rechte sind ohne die letzte nicht zu gewähren.
· SecurityService
Führen, exportieren und planen die Report Studio-Berichte
Der Service Hälfte des Rahmens: der Katalog, der Lauf, die Picker, vier Exporteure, gespeicherte Ansichten und Zeitpläne. Die Genehmigung ist auf jedem Weg explizit, denn dieser Dienst läuft GenehmigungAll bei die Filterkette. Zwei Rechte sind immer im Spiel - VIEW_REPORTS öffnet das Modul, und das eigene Domainrecht des Berichts entscheidet, ob dieser Bericht erstellt werden darf und wie viel von der Organisation, die es abdeckt. VIEW_REPORTS allein lässt niemanden Daten zu Das ist, was macht die App sicher zu schalten für einen Mieter. ReportScopeResolver liest die bestehenden OWN/ALL Geschwisterrechte der Plattform und Nichts für die Berichterstattung erfunden. Ein Bericht, der kein engeres Recht benennt, ist Organisationsweit und sagt das in einer Notiz das Studio und jeder Exportdruck wörtlich - eine Summe produziert unter "Ihre eigenen Platten nur" ist nicht das gleiche Dokument als eine über die gesamte Organisation produziert, und ein Leser kann nicht sagen sie sonst auseinander. sanitise() ist die Grenze, die eine gespeicherte Ansicht davon abhält, eine benutzerdefinierte zu werden Bericht: Eine Anfrage kann nur unter dem auswählen, was die Definition bereits erklärt hat. Es Streifen unbekannte Spalten, Gruppierungsstufen und Parameter, aber absichtlich verlässt eine unbekannte Spalte in einem FILTER für die Abfrage-Engine zu verweigern - Dropping ein verstecktes Spalte ändert die Darstellung, indem ein Filter entfernt wird die Antwort. Exporte lesen die gleiche ReportResult der Bildschirm wurde aus und Format durch der gleiche ReportFormat-Deskriptor, also eine Figur in einem PDF und der Zelle, aus der sie kam kann nicht widersprechen. Die Arbeitsbucher schreibt Zahlen als Zahlen mit einer Formatmaske eher als vorformatierte Strings, das ist der Unterschied zwischen einer Tabellenkalkulation und ein CSV mit einer anderen Erweiterung. Das PDF trägt die Organisation, die Zeit, der Datenumfang, wer lief es und Seitennummern. Ein geplanter Bericht läuft als seine OWNER, mit Rechten zur Laufzeit neu beachtet, so Das Entheben des Zugangs stoppt einen Zeitplan bei seiner nächsten Auflage, anstatt wenn eine Sitzung läuft. nextRunAt rückt vor der Auslieferung vor: Es gibt kein ShedLock in dieser Flotte und Repliken: 1 ist alles, was einen doppelten Senden verhindert. pdfbox und poi-ooxml werden HIER und nicht zu Kamo-Shared-Bibliothek hinzugefügt, die jeder Service-Komponenten-Scans - sie würden sonst in 60 Bilder versenden, die nie einen Bericht geben. kamo.reporting.enabled ist in der ConfigMap und nirgendwo sonst; diese Eigenschaft ist es, was verhindert, dass eine Berichtsbohne gebaut wird in Dienstleistungen, die sie nicht befriedigen können. Auch benennt einen abgestandenen Eintrag in ************ ec2ae5e umbenannt ************ zu #syncLabels ohne Umbenennung Grundlinie, die die Ratsche am Ursprung/Haupt in beide Richtungen bei einmal. Der Handler löst einen Anrufer - getOrganizationFromRequest, 401 null - also ist dies eine Umbenennung, keine Erweiterung.
· kamo-shared-library
Das Report Studio Framework und neun Berichte darauf aufbauen
Ein Bericht ist jetzt eine Java-Klasse hier und nichts anderes. BerichtDefinition beschreibt, was ein Bericht ist, was er gefragt werden kann, welche Spalten er hat und wie jeder sollte gelesen werden; das Frontend macht, was auch immer erklärt, so dass ein Bericht benötigt keinen Endpunkt, keine Seite und kein TypeScript. Die Stücke, die das Gewicht tragen: - Reportcapability ist der Grund, warum das Frontend generisch sein kann. Ein Bericht, der erklärt nur FAVORITE und SCHEDULE bekommt keinen Filterbauer, keinen Spaltenmanager und keinen Grouping Panel - ManagementOverviewReport existiert teilweise als kanarisches. - ReportFormat ist ein Deskriptor, nie ein gerenderter String, also eine Währung in der Tabelle, der Chart-Tooltip, der CSV und das PDF können nicht widersprechen. - ReportSecurityScope kommt von jedem Bericht OWN Domain-Rechte, nie von einem richtig berichten. Ein Verkaufsbericht erweitert sich auf VIEW_ALL_LEADS genau wie die Führung Grid tut, so dass die Berichterstattung kann nie zu einem Weg um das Sicherheitsmodell zu umgehen. - ReportQuerySupport stellt das Zielfeld an die erste Stelle, lehnt eine unbekannte Spalte ab Schlüssel, anstatt es fallen zu lassen, und links-joins jeder Verein - ein impliziter Beitritt ist ein INNER, und die Reihen mit nichts auf der anderen Seite sind die Jemand sucht. - ReportAggregator behält eine Null-Gruppe als eigene Gruppe, so Subtotals noch hinzufügen bis zur Gesamtsumme. - Termine lösen sich in der Zeitzone des MEMBER und sind halb offen. Um 03:30 UTC auf 1. September ein Mitglied in Los Angeles ist noch im August. Jede Berichtsklasse beinhaltet @Component AND @ConditionalOnProperty. Das ist nicht Dekoration: jeder Dienst scannt com.kamo, erklärt aber seine eigene explizit @EnableJpaRepositories Liste, so dass eine nackte @Component brauchen einen EntityManager ist in allen von ihnen gebaut und CrashLoops diejenigen, die es nicht befriedigen können. Die Eigenschaft ist nur in SecurityService gesetzt, und ReportBeansAreConditionalTest scheitert der Build, wenn ein Bericht es vergisst. Reports ist eine App (ServiceType 32) mit fünf Rechten (295-299). VIEW_REPORTS ist die Modultür und gewährt keine Daten von selbst. Die Namen VIEW_REPORTS und EXPORT_REPORTS existierte in den IDs 1 und 2 bis 2026-08-14, als sie entfernt wurden zusammen mit den Stub-Seiten hinter ihnen; Rechte bestehen als getId(), so dass keine Gewähr gemacht, bevor dann zu diesen zu lösen. PhiModule.REPORTING ist PERMITTED - die Maschine liest In-Cluster und speichert keine kopieren. Export und Terminplanung verlassen die Grenze und werden von ihrer eigene Rechte und durch EMAIL.
· kamo-internal
Der Importeur, und drei Dinge, die es uns gelehrt
Veröffentlicht den Korpus, indem Sie die echte KBService API fahren, anstatt zu schreiben Zeilen -- ein direkter INSERT überspringt die Versionsreihe, die kb.article.published Veranstaltung das ist das einzige, was die Indizierung eines Artikels für die KI-Assistent, und die Übersetzung fan-out. Ein Import, der erfolgreich aussah und verließ "Train AI With Dieser Artikel" nichts zu tun auf dreihundert Artikel ist die Art von Versagen Einen Monat lang wird es niemanden gemerkt. Drei Mängel ist aufgetaucht, alle hier behoben: 19-stellige IDs dürfen niemals eine JavaScript-Nummer berühren. Nummer('1168485648209608710') ist 1168485648209608700. Eine Sitzung mit diesem org id authentifizierte perfekt und löst eine Organisation, die nicht existiert -- die Übersicht kam leer zurück und las sich wie "die Wissensbasis ist leer". Die gleiche Falle ein zweites Mal auf dem Artikel uid, wo nachgeordnete beantwortet 403 weil der Elternteil Die ID war gerundet worden und hatte nichts mit nichts gepasst. Session-IDs sind jetzt in die JSON als rohe Literale, und Artikel-IDs werden aus dem Antworttext vorgelesen JSON.parse kann sie berühren. Der Null-Elternteil einer Kategorie ist der Abschnitt, nicht die Wurzel. Nichtsendet hätte veröffentlicht sechzehn Geschwister Top-Level-Artikel, und die Marketing-Website -- die fragt nach dem Baum unten help-center -- hätte eine leere Seite gezeigt. kubectl Port-forward überlebt nicht ein paar tausend Anfragen. Verkehr Ausfälle jetzt erneut mit Backoff versuchen; HTTP-Fehler nicht, weil die Wiederholung eines 403 Viermal dauert es nur länger, um zu scheitern. Der Autor ist Sage, der bereits den Help Center-Artikel verfasst hat. Ein Artikel Autor und letzter Redakteur kommen aus der Session, die es geschrieben hat und von Name auf jedem Artikel in der Konsole.
· kamo-shared-library
Bewahren Sie die Angebots-Kontakt-Regel eines Marktes durch und lehnen Sie eine halbstredige ab
Die DTOs trugen ZitatContactMode und ZitatContactMemberId aber kein Schriftsteller gelesen sie, so dass die Einstellung rund durch die Form strich und stillschweigend fallen gelassen wurde. Sowohl die Erstellungs- als auch Update-Pfade gelten nun. Das Mitglied wird nur unter EXPLICIT gehalten und anderweitig abgeräumt, so ein Markt, der Schalter zurücklassen keine Person zurück, um durch einen späteren Flip wieder eingesetzt zu werden, und EXPLICIT Benennung niemand wird abgelehnt, anstatt leise herabgestuft leise Version druckt einen anderen Namen auf das nächste Zitat als wer die gespeichert Form erwartet. Die Suche ist org-scoped: Mitglieder-IDs sind weltweit einzigartig, also ein Unausgehweigt würde man einem Mieter den Personal eines anderen Mieters auf seinem eigenen Dokument benennen lassen.
· kamo-internal
Wählen Sie, wen ein Zitat als Ansprechpartner des Kunden benennt
Der Marktdialog gewinnt "Quote's Future Contact" unter Enable-Zitate: the lead's zugewiesener Agent, eine Auswahl pro Angebot, oder eine explizite Person mit der gleichen AgentSelector der /bles Filter verwendet. Unter Adjustable Per Quote der Herausgeber wächst ein Kontakt-Abschnitt, gesät aus dem Schnappschuss der Server nahm, wenn das Zitat wurde angehoben - das ist der Agent des Leads, so öffnet es sich auf die Person bereits dort. und speichert die Änderung mit dem Rest des Zitats. Die drei Kunden-Seite Entscheidungen bewegte sich aus zwei großen Komponenten in app/lib/quotes/quoteContact.ts. Sie wurden sowohl in den Bildschirmen und den Bildschirmen gefragt Meinungsverschiedenheiten sind unsichtbar, bis ein Dokument die falsche Person ausdruckt; als ein Modul sie sind auch prüfungsfähig, die inline ternaries in einem 1300-Linien-Dialog nicht waren. Die Vorschau zeigt einen neu gewählten Kontakt nach Namen mit Titel, Telefon und E-Mail blank: diese werden Server-Seite von der Mailbox und der Erweiterung dieses Mitglieds aufgelöst, so tragen die Nummer der Voriste unter einem neuen Namen wäre eine Lüge der Unerfahrene Wechselbanner entschuldigen nicht.
· SecurityService
Die Kontaktregel eines Marktes überall dort, wo ein Zitat jemanden benennt
Das Personal Detail, die Mitarbeiter-Vorschau und die eigene Seite des Kunden lösen nun die Kontakt über eine Methode, so dass sie nicht darüber, wer auf der gedruckt ist gleiches Dokument. Unter ASSIGNED_AGENT ist das der Agent des Leads pro lesend, wie vorher; unter den beiden anderen Modi ist es der Schnappschuss, den das Zitat trägt, mit der Lead-Agent als Fallback für ein Zitat angehoben, bevor die Einstellung existierte oder eine, die jemanden benennt, der inzwischen gegangen ist. Erstellen Sie Briefmarken den Schnappschuss vom Markt und nicht von der Anfrage-Einband, und Update gilt ein neuer Kontakt nur, wenn der Markt läuft ADJUSTABLE_PER_QUOTE und das Mitglied gehört zum org - einem Markt, der einen expliziten Kontakt benennt bedeutet es, und ein von Hand gemachter PATCH ist, wie dieses Versprechen sonst gebrochen würde. Die fünf Handler von QuoteController haben Kunden/Agenten/einstellbare separat erstellt; teilen sie sich jetzt eine Mitarbeiter-Ansicht, was die drei konsistent hält.
· kamo-internal
Der Artikel Corpus, und die Pipeline, die es baut
307 Artikel in 16 Kategorien und 29 Abschnitten, ab dem 248 Entwurf Artikel unter Dokumentation/ plus 59 hier geschrieben: jede Kategorie und Abschnitt Landing Page, ein Getting Started Set für die Onboarding der Entwürfe nie erreicht, eine Plattform-Kategorie der Quellordner hatte immer nur eine README für, und die beiden Mitglied-Kreation Artikel, die einfach fehlten. Vier Stücke: lib/markdown-to-lexical Markdown auf die Form der Wissensbasis speichert. Der Knotensatz ist eine Whitelist dessen, was beide Renderer bereits behandeln, weil der öffentliche Renderer zeichnet einen unbekannten Knoten als bloßes div -- kein Fehler, nein Warnung, ein Absatz, der leise sein Styling auf einer Seite verloren hat niemand ist Zuschauen. Listen waren der schwierige Teil: eine leere Zeile darf nicht enden (die Entwürfe haben Screenshot-Lücken zwischen den Schritten, und das Ende dort startet die Nummerierung bei 1) und ein eingerückter Gegenstand ist eine Unterliste, nicht ein Satz angehängt zum Schritt darüber. lib/clean-source Was ein Entwurf aufhören muss zu sagen, bevor er veröffentlicht wird. Berechtigungskonstanten werden zu den Wörtern auf dem Bildschirm; Klammern benennungen eine .tsx-Datei oder ein Entity-Feld gehen vollständig; die Überprüfungs-Checkbox und die Draft-Methode Callout gehen; und Querverweise werden umbenannt, anstatt gelöscht, so "sehen, wie ich meine Zeitkarte anzeigen?" Punkte an der Artikel veröffentlicht Name anstelle von einem, der nicht mehr existiert. lib/titles Titel zu veröffentlichten. How-Tos nehmen eine Regel; die 53 Konzepte werden von Hand benannt, weil eine Regel, die drehen könnte "Was ist die Differenz zwischen Status und Sub-Status?" in einen Seitennamen hätte mehr Ausnahmen als Fälle. Schlär werden auf Kollisionen im gesamten überprüft tree bevor ein Import läuft -- die Datenbank schränkt sie pro Organisation ein, nicht pro Elternteil. Build-Bezüge Die siehe-auch-Grafik, erstellt ungerichtet, weil Links symmetrisch im Produkt. 594 Links, 3 bis 6 pro Artikel. Aus dem Entwürfe eigene verwandte: Front Materie, die jemand während des Schreibens gewählt; Aufgestockt von Position im Baum. Textähnlichkeit ordnet Kandidaten, aber qualfiziert sich nie für sich allein -- "Notizen" und "Wie Sie suchen und filtern Leads" teilen jedes Wort über die Suche und haben nichts mit jedem zu tun andere. Keine Seite ist mit dem eigenen Elternteil oder Kind verwandt: ein Abschnitt bereits listet seine Artikel auf, und ein Artikel hat bereits Brotkrumen. 57 Tests, und vitest's include list wächst eine Linie, so dass sie tatsächlich laufen.
· kamo-shared-library
Ein Markt entscheidet, wen ein Zitat als Ansprechpartner des Kunden nennt
QuoteContactMode gibt einem LeadMarket drei Antworten: der zugeordnete Agent des Leads (aufgelöst pro gelesen, das ist, was jedes Zitat heute tut und was eine Null-Spalte bedeutet), eine explizite Person oder eine Wahl, die auf jedes Zitat getroffen wird. Die beiden letzteren sind schnappte auf Quote.contactMemberId, wenn das Zitat angehoben wird, also eine Änderung Der Geist des Marktes schreibt nie den Kontakt um, den ein Kunde bereits gegeben hat, und Revision trägt den Schnappschuss nach vorne - es ist das gleiche Angebot. QuoteService nimmt den gelösten Kontakt als Argument, anstatt ihn zu lesen von der Anfrage-Körper: Dieses Modul erreicht die Führung nur durch Soft-ID, und einen Wert Ein Client könnte eingestellt werden, würde jeder auf einem Dokument genannt werden lassen. Update ehrt es nur wenn der Anrufer einen passiert, so wird ADJUSTABLE_PER_QUOTE durchgesetzt serverseitig statt durch den Editor, der ein Feld versteckt. agentForMember wird genannt, anstatt überlastet . agentFor(null, ...) wäre zweideutig an jedem Anruf-Website vorbei an einer buchstäblichen Null - und die Lead-Cing-Form jetzt delegiert es, so dass die beiden nicht auf einem gedruckten Dokument driften können.
· InitializerService
Spalten für den Angebotskontakt eines Marktes und den pro-Quote-Schnappschuss
Ein Markt benennt seine Antwort in QUOTE_CONTACT_MODE (+ QUOTE_CONTACT_MEMBER_ID wenn Diese Antwort ist eine explizite Person), und jedes Zitat hält den Kontakt war es angehoben mit in **************** Beide nullbar ohne Standard: ein Null-Modus liest sich wie der zugewiesene Agent und ein Null-Schnappshot fällt zurück, so dass jeder bestehende Markt und Zitat ist unverändert, bis jemand anders wählt.
· kamo-internal
Kuratieren Sie die dazugehörigen Artikel eines Artikels
KbRelatedPanel ist eine Komponente an drei Stellen: Es kuratiert den Komponisten und der Redakteur, und liest zurück auf den Artikel Ansicht. Ein Artikel hinzufügen hier fügt auch diese zu diesem Artikel hinzu, und das Panel sagt es -- sonst die zweite Hälfte des Schreibens sieht aus wie etwas, das einen Artikel redigiert, nicht haben getan. Zwei Modi, weil Links ausgewählt werden können, bevor der Artikel existiert. Mit einem Guid das Panel besitzt seinen Zustand und schreibt jede Änderung gerade durch: eine Neuordnung verloren zu einem vergessenen Speicher ist eine Nachordnung niemand macht zweimal. Ohne einen, die Komponist hält die Auswahl und PUTs es nach erstellen, als separater Aufruf für der gleiche Grund, der für eine Überprüfung eingereicht wird -- ein Link, der nicht gespeichert wird, sollte Lassen Sie den geschriebenen Artikel hinter sich, anstatt ihn damit abzubauen. Vorschläge kommen vom Server, der Geschwister vor Artikeln rangiert, die lediglich teilen Wortschatz, und filtert sie durch den gleichen Lesebereich wie jede andere andere lesen. Das Panel behält die Antwort des Servers und nicht die Liste, die es gesendet hat, so ein guid der Server fallen gelassen wird nicht angezeigt, wie gespeichert.
· KBService
Verwandte Artikel, und ein öffentlicher Weg, der den Baum hält
Drei Dinge, die alle die gleiche Seite erreichen. Verwandte Artikel. KbArticleRelationService besitzt das symmetrische "see auch" Links: Liste, gesetzt, vorschlagen, und eine klare, die auf weiche löschen läuft, so dass ein Ruhestand Artikel verweilt nicht in der Liste von jemand anderem und kürzen sie stillschweigend. Lesen Sie durch KbReaderScope -- eine ungefilterte Beziehungsliste nennt die Titel von genau den Artikeln, die das Publikum Flaggen verbergen, die das Orakel ist lesbar() Antworten 404 zu vermeiden, Ankunft durch eine andere Tür. Die Öffentlichkeit Lesen ist noch schmaler: veröffentlicht und nur öffentlich zugänglich, also eine Marketing-Seite kann nicht einen Besucher mit nur Mitgliedsmaterial verbinden. Öffentlicher Weg. KbPublicArticleDTO trägt jetzt Pfad und Vorfahren. Weg ist die Schneckenkette BELOW die Sektion Wurzel, so /help-center/help-center ist unrepräsentabel und ein Verbraucher kann eine verschachtelte Verbindung anstelle einer flachen bauen. Das ist nicht kosmetisch: Artikel-Suche löst auf dem letzten Pfad-Segment, so ein Link aus der nackten Schnecke gebaut macht immer noch die richtige Seite und die verlorene Hierarchie zeigt sich nirgendwo, bis jemand liest die Sitemap. Ein Lese statt vier. buildPublicTreeIndex ersetzt die separate buildUidToGuidMap / buildPublicChildrenMap Scans -- eine einzelne Artikelseite war machen drei volle Tisch liest plus eine vierte für die Muttergesellschaft, alle Projektionen von die gleiche Liste. Brotkrumen und dazugehörige Karten jetzt mit dem Artikel übersetzen anstatt Englisch unter einem übersetzten Körper zu bleiben. 98 Tests, davon 30 neu: Bahnform und Zyklen in KbPublicPathTest, Spiegel Wartung und Umfangsfilterung in KbArticleRelationServiceTest.
· InitializerService
Migration für KB_ARTIKELE_RELATIONEN
Spiegel KbArticleRelation in kamo-shared-Bibliothek. Geschrieben eher als links zu ddl-auto, so dass die UID und DATE_CREATED defaults live in der Datenbank, die es ist, was eine einfache SQL-Einfügung arbeiten lässt, ohne sie zu benennen. Der einzigartige Index ist auf dem bestellten Paar, nicht der ungeordnete: (A,B) und (B,A) werden beide erwartet, zu existieren, und die Beschränkung der ungeordneten Paar würde lehnen Sie die Spiegelzeile ab, die der Dienst auf jedem Link schreibt. Bereits angewendet auf die Produktion durch die Durchführung dieser Aussagen als kamo App Benutzer; sie sind idempotent, so dass eine spätere vollständige KI-Lauf ist eine No-Op.
· kamo-shared-library
Artikel-zu-Artikel-Beziehungen
KB_ARTICLE_RELATIONS hält die kuratierten "siehe auch" Links zwischen Artikeln in eine Wissensbasis. Reihen sind pro Richtung und das Paar wird symmetrisch gehalten durch KbArticleRelationService, das ist das einzige, was erlaubt ist, sie zu schreiben -- ein halbgeschriebenes Paar zeigt den Link auf einem Artikel und nicht auf dem anderen, und ist unsichtbar von der Seite, die noch ihre Reihe hat. Zwei Reihen statt einer, so dass jede Richtung ihren eigenen SORT_ORDER trägt: Artikel bestellt die Liste unter ihm, ohne das andere Ende zu stören, und jeder read bleibt eine einzige indizierte Lookup auf ARTICLE_ID. Die DDL ist bereits gelandet ************ in KamoInitializerService, angewendet auf Yugabyte als der kamo App Benutzer).
· SecurityService
Seite und suchen Sie das org-Verzeichnis, und stoppen Sie zwei Reihenbauer Drifting
Die Netzwerke endpoints antworten mit dem ganzen Set in einem Körper. Das ist in Ordnung für eine Mitglied von einem Dutzend Orgs und unmöglich für einen Betreiber auf einem Anwesen soll erreichen um zwanzig zu zeigen. Fügt GET /org/network-Verzeichnis hinzu, das passt, Bestellungen und Fenster in der Datenbank und gibt eine Seite zurück. scope=all ist genau auf der Prüfung, die all-networks gilt, . SYSTEM_USER durch PlatformRightsService - also funktionieren alle drei Wege nach rechts weiter und die Liste kann nie breiter sein als die Handlung dahinter. Die gleiche Antwort reitet zurück wie kannEnterAsSystemMember, so dass ein Client seine Zeilen und seine Buttons aus einer Antwort erstellt. my-networks und All-Networks hatten jeweils einen handgeschriebenen Reihenbauer, und die Kopien hatten bereits gedriftet: All-Netzwerke emittiert nie Primärfarben, logoUrl oder logoSimpleUrl, so /network zog jede org ein Operator war nicht ein Mitglied in der Konsole Standard Akzent statt der Org eigenen. Nichts berichtete es - die Schlüssel fehlten einfach und der Fallback des Kunden hat den Rest getan. Alle drei teilen sich nun OrgNetworkRowBuilder.
· kamo-marketing
Starter Basis bis $10 auf jeden Begriff
69 / 59 / 49, von 59 / 49 / 39. Die Verpflichtungsleiter ist vom Design unberührt. noch genau $10 off für ein Jahr und $20 für drei - das ist der Standard jeder Oberfläche gibt jetzt in Dollar statt als Prozentsatz. Fallbacks in fünf Dateien bewegen sich mit ihm, und Check-Preis-Tokens zieht die Überholte Zahlen, damit niemand einen alten Starter-Preis wieder in einen Vergleich einbringt Seite. Die drei Metadaten Beschreibungen zitiert "$39/seat/mo" als der von-Preis; die Die Zahl, die ein Suchergebnis anzeigt, ist ein Preisanspruch wie jeder andere. Der Bundle-Kommentar in PricingClient behält seine $53/$59 Zahlen, ist aber jetzt datiert: sie sind der Grund für eine vergangene Entscheidung, nicht ein Live-Zitat, und eine undatierte abgestandene Preis in einem Kommentar liest sich als ein Fehler für die nächste Person. Verifiziert gegen den Live-Katalog statt der Fallbacks: Starters dreijährige Sitz Renders Was $49 / Now $32.83, und der Homepage-Streifen stimmt mit / Preisgestaltung.
· kamo-internal
Das laufende Angebot, auf Plans & Billing und auf der Startrampe
Die Konsole zitiert Listenpreise, während kamocrm.com beworben ein Drittel aus der gleichen Plan, denn nichts in Kamo-internal konnte sehen, dass ein Angebot existierte. Es jetzt liest das aufgelöste Angebot von BillingService und zeigt, was es mit dem Preis macht: ein Banner über dem Planpicker mit einem Countdown und was es spart auf der Bestellung in Hand, und jede abgedeckte Plankarte durch ihre engagierte Rate geschlagen angeboten. Die Schätzung unten bereits Preise, was die Karte zeigt, so dass die beiden kann nicht widersprechen. Die Startrampe gewinnt eine einzeiliche Pille neben der Testpille - zu seiner LINKE, und gezeigt ob die Studie Pille ist oben, da eine Organisation Vergangenheit seiner Studie mit keine Abonnement ist genau, wer das Angebot ist. Es ist darauf gedrängt, in der Lage zu handeln it: ein Mitglied, das seinen eigenen Sitz kaufen kann, oder ein Eigentümer, der die Organisation, und wer nicht bereits zahlt. Dieser letzte Test liest sich coverage.source, nicht coverage.paidBy bezahlt Von der WHO deckt den Sitz, und Jemand, der auf dem Free-Dault-Abonnement der Organisation sitzt, liest ORGANISATION, während sie auf gar keinem bezahlten Plan sind. Jemandem ein Upgrade anbieten bereits zahlen ist der schnellste Weg, um ein Banner unsichtbar zu machen. planOffer.ts hält die Arithmetik, so dass die Pille und die Konsole nicht driften können, und weil diese Zahlen Menschen Geld ausgeben: der Prozentsatz kommt aus der Rate für den Begriff COMMITTED zu, nie der Monat-zu-Monat-Listenpreis, passend ************ 18 Prüfungen decken die Bestellung, den Sitz und Begriff Skalierung, dass ein ungedeckter Plan in Ruhe gelassen wird, und dass das Enddatum ist die am letzten Tag funktioniert der Code und nicht der erste Tag, an dem er nicht ist. Check-Server-Timestempel erwischten die ablaufende Parse: "Neues Datum(\-${d-T23:59:59Z\) explizite UTC und korrekt, aber der Wächter kann es nicht von der naiv-saitischen Fehler sagen es existiert zu fangen, und ein Wachmann Menschen lernen, mit zu argumentieren ist schlimmer als der Fehler. Es geht durch parseServerDate jetzt, die einen Datums-nur Wert bei UTC Mitternacht hält - so ist die Frist, dass plus einen Tag, die auch klarer darüber, warum.
· BillingService
Servieren Sie das laufende Angebot, und sagen, ob ein Sitz ist eigentlich PAID für
Fügt GET /markets/{id'/promotion hinzu. Es gab keine Möglichkeit für eine In-App-Kauffläche zu wissen, dass ein Angebot überhaupt lief - die Marketing-Website lesen Promotions durch die Öffentliche Katalog Proxy, und kamo-internal hatte keine gleichwertige, so dass die Pläne & Abrechnung Konsole zitiert Listenpreise, während kamocrm.com bewarb ein Drittel aus dem gleichen Plan. Es gibt das Angebot RESOLVED zurück, nicht die rohe Reihe. Jeder Verbraucher der rohen Form hat die gleichen drei Dinge neu zu machen - ob es heute live ist, welche Pläne es deckt durch Code statt durch UUID, und welche Zyklen - und jeder, der tat so bekam mindestens eine falsche: die Marketing-Website erweitert einen Zwei-Plan-Code in eine Jeder-Plan-Code durch den Vergleich UUIDs gegen Plan-Codes, und sein Held gefiltert auf der Rabatt TYPE und still beworben nichts für Monate. Leben kommt von AbonnementPromotions.check, das gleiche Prädikament berechnetPrice gilt, wenn es tatsächlich Rabatte eine Bestellung, so dass eine Oberfläche kann nicht werben Sie einen Code-Checkout wird Decoder. Pläne kommen in Display-Reihenfolge zurück, weil die Oberflächen sie in ein Satz und "Business and Starter" liest sich, als ob die Ränge bergab liefen. /self/coverage jetzt auch "Quelle". "PatentBy" Namen Wer deckt einen Sitzplatz, die ist eine andere Frage, als ob etwas dafür bezahlt wird: ein Mitglied auf das Free-Denfault-Abonnement der Organisation liest paidBy=ORGANIZATION und ist auf überhaupt keinen bezahlten Plan. Alles, was ein Upgrade bietet, muss in der Lage sein, diese zu sagen auseinander, und paidB kann nicht.
· kamo-marketing
Das Angebot erreicht den Helden und den Preisstreifen
Der Held hatte schon ein Countdown-Band, und es hatte seit der Katalog auf einen Prozentsatz verschoben: "PickHeroOffer" gefiltert auf FLAT_PER_SEAT weil die Kopie des Banners "$18/Sitz jeden Monat für 12 Monate" war. Eine zweite, engere Definition von "was Angebot läuft" war das ganze Problem, so ist es gegangen - der Held, die Preisseite, die Angebotsseite, der Taschenrechner und jetzt die Homepage Streifen alle lesen Sie die eine "PagePromotion" lösenPromotion kehrt zurück. Das Band wird auf den Prozentsatz neu angepackt, Links zu /preisen/Angeboten und zählt bis zum ENDE des letzten Tages (Mitternacht lief es einen Tag zu früh). Es trägt die ember Akzent das Angebot trägt auf jeder anderen Oberfläche, so ist es erkennbar als die Förderung, bevor ein Wort davon gelesen wird; seine CTA-Tinte wird daran gemessen, gradienten anstatt links weiß, die unter 4,5:1 an beiden Enden ist. Der Homepage-Streifen gibt nun die gleiche PlanPrice-Komponente / Preisgestaltungen wieder, jetzt, und was Sie sparen - anstelle seiner eigenen handgerollten Leiter über annualPreis. Beide öffnen auf HOME_TERM, einmal in plan-preisgeätzten.ts genannt, weil drei Oberflächen In der strittigen unabhängigen, und die CTAs des Streifens tragen den Code auf den Plänen die bieten Abdeckungen. Plan-IDs auf einer gelösten Promotion kommen jetzt in CATALOG-Bestellung zurück. Die API kehrt zurück sie in Abfrage-Bestellung - Business vor Starter - und der Held schreibt sie in eine Satz, so heißt es "auf jedem Sitz von Business und Starter", als ob die Reihen lief bergab. check-i18n-Tasten konnten Hero.tsx AT ALL nicht sehen: es bindet .const [t, slideshow] = warten **************** ...]) und die Wache nur verstanden "Const t = ".... Drei fehlende Schlüssel in der meistgelesenen Komponente der Website, unsichtbar an die Wache, deren gesamter Zweck ist der Fang einer rohen Schlüsselbahn, der sichtbar wird Text. Es versteht jetzt die destrukturierte Form, und - noch wichtiger - eine Datei dass deutlich übersetzt, aber ergibt keinen Namensraum wird BERICHTSBERICHT anstatt übersprungen, so kostet die nächste neue Bindungsform ein Wach-Update statt Versand "home.heroOfferCta" in den Helden. Die Abdeckung ging von 1044 Schlüssel auf 1073. Check-Plan-Preise wachsen auf 55 Schecks, die abdecken, wie die Pläne eines Angebots geschrieben werden out und lösenPromotion gegen die Form der API tatsächlich zurückkehrt dass eine Beförderung, deren Pläne nicht gelöst werden, abgelehnt wird, anstatt zu erweitern. Verifiziert gegen den Live-Katalog statt der Fallback: Homepage und /Preisgestaltung beide machen $0 / $26.13 / $66.33 / Custom.
· kamo-marketing
Eine Drei-Term-Engagement-Schiene, und eine Förderung der Seite kann beweisen
Neue Preise: Starter $59/$49/$39 und Business $119/$109/$99 pro Sitz pro Monat monatliche, jährliche und dreijährige Laufzeiten. Die Schiene ersetzt das Monats-Jahr-Paar und seine "Save 18%" Pille - ein Prozentsatz war die falsche Einheit für eine flache $10/$20 pro Sitz, so dass die Pille zitiert, welche Plan "find" geschah, um zuerst zurück, und ein toggle kann ohnehin nicht drei Optionen ausdrücken. Sein Daumen ist ein Vorhängeschloss, das als der Begriff verlängert sich, das ist die Beschriftung darüber ("Lock in your rate today!") wörtlich gemacht. Das Angebot Band gibt die laufende Promotion einmal, über den Karten, und sagt, es ist automatisch angewendet. Das ist eher wahr als dekorativ: der Plan CTAs jetzt ?code=, das RegisterFlow bereits in sp_checkout_code liest, und nur auf Pläne, die das Angebot tatsächlich umfasst - Senden AUTStN in die Kasse von der Free oder Enterprise-Karte würde einen Rabatt der Server dann Decoder versprechen. Starter und Visitenkarten zeigen Was / Jetzt / was Sie speichern, und die Aktion kommt von der TERM-Rate, nicht der Listenpreis - zuerst festlegen, dann nehmen Sie den Prozentsatz aus die bereits reduzierte Nummer, passend **************** Vier Oberflächen waren jeweils dabei diese Arithmetik selbst und nicht einverstanden, so dass es bewegt sich in app/lib/plan-pricing.ts und sie alle lesen die gleiche Antwort. Die calculator Prozent prom Promo wurde aufladen gegen Add-ons, Mailboxen, Erweiterungen und Speicher sowie Sitze, so dass seine Schätzung kam unter dem, was der Server Rechnungen; es ist jetzt auf die Plan Sitze wie der Server ausgeweitet. / Preisgeld/Angebot wird umgebaut, was auch immer die Aktion der Katalog trägt. Es hatte "18DOFF / $18 off Business" im Client UND in seiner Seite Fallback-und über neun Wörterbuch-Strings, und der Katalog hatte sich an allen dreien bewegt. Außerdem: package-lock.json war nicht synchron mit package.json (@swc/helpers missing), so npm ci" - das ist, was die Dockerfile läuft - gescheitert an der Hauptleitung. Das Bild bauen wurde unabhängig von dieser Arbeit gebrochen.

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