Changelog
Cosa c'è di nuovo.
Ogni miglioramento alla piattaforma, come si spedisce.
· VOIPService
Servire l'URL di webhook in entrata, da una definizione
Un amministratore che aggiunge un provider di testo deve incollare l'URL in entrata di Kamo nel La console del vettore, e niente gli disse cosa fosse. Manca quel passo. nessun sintomo sul lato nessuno prova prima: i messaggi mandano perfettamente e ogni La risposta è calata. Risponde. Servito piuttosto che hardcoded nel browser per il motivo URI redirect di RingCentral è servito: il valore proviene da una proprietà di distribuzione, e una copia nel frontend mantiene con sicurezza visualizzando il vecchio ospite dopo che il vero si muove. L'URL ha ora una definizione. TelnyxProvisioner WRITES nel cliente profilo di messaggistica e lo schermo di impostazioni SHOWS a qualcuno incollarlo da mano; quando si trattava di due corde nulla avrebbe fallito se si fossero allontanati — un amministratore sarebbe stato semplicemente istruito per configurare un URL niente Ascolta. InboundWebhookUrls è quella definizione, e il suo test afferma che ogni slug esso costruisce risolve di nuovo attraverso InboundSmsNormalizer, così i due lati concordano per costruzione piuttosto che da un tavolo qualcuno deve ricordare di aggiornare. Il punto finale riferisce anche se l'ingresso di un vettore è autenticamente cablato — un E un parser. Consegnare a qualcuno un URL per un vettore i cui carichi di pagamento nulla può leggere sarebbe un'istruzione per configurare qualcosa che poi rifiuta ogni messaggio, che è peggio di dire chiaramente che non è ancora supportato.
· kamo-internal
Una impostazione che copre ogni numero su un sistema telefonico
Il progetto di routing approvato aveva tre livelli e solo due erano raggiungibili. Un numero potrebbe nominare il proprio fornitore di testo, e in mancanza che il server del telefono era ha chiesto di scriversi — ma il livello medio, "a meno che un numero non dica altrimenti, il nostro testi passano attraverso Telnyx", è stato implementato in TextRouter e impostabile da nessuna parte. An organizzazione con duecento DID su un FreePBX ha dovuto modificare duecento righe a esprimere un fatto, che è il lavoro di data-entry lo schermo Numeri doveva evitare. Ora è una discesa sul server stesso del telefono. Tre cose che valgono la denominazione: - La fusione funziona LAST in salvo(). Ogni ramo del fornitore sopra di esso ASSIGNS configJson piuttosto che fondersi in esso, quindi la prima versione di questo era silenziosamente caduto su server Telnyx — l'unico fornitore il cui ramo accade venire dopo dove originariamente sedeva. - "Niente - impostare ogni numero singolarmente" piuttosto che un'opzione vuota, perché a discesa vuota legge come unset piuttosto che come una decisione. - La chiave è una stringa in due lingue in due repository, e la deriva è SILENT: l'impostazione salva, la discesa mostra ancora il fornitore che era scelto, e ogni numero che si basa sul fallback tranquillamente cade fino testo tramite il server del telefono — che per FreePBX significa non testo affatto. Quindi... il modulo importa DEFAULT TEXT INSTANCE KEY, e il testoRouteContract.test.ts pins contro TextRouter.java il modo di fatturazioneCycles.test.ts pins l'enum di fatturazione. Quella guardia e' volutamente onesta su quello che prova. Il cross-repo mezzo skips ogni volta che il controllo del fratello è assente o stallo — che è la maggior parte del tempo — così si chiama "quando quella fonte è disponibile" e un passaggio lì significa "non contradditto", mai "verificato". L'affermazione che corre sempre è quella che ha preso un rinomina quando la guardia è stata sorretta rompendolo di proposito. Documento di progettazione aggiornato: i due limiti elencati come differito — un solo vettore l'organizzazione in grado di risolvere una risposta, e MMS attraverso Telnyx — sono entrambi chiusi in voipservice 8eaebfb.
· kamo-internal
Reagire a un cambiamento di stato, non solo a un commento
"🎉" su un evento fisso è la cosa più naturale che chiunque voglia dire in un thread bug, e l'offerta di reazioni solo su commenti significava digitarne uno per dirlo. centrato sotto la pillola dell'evento, e nascosto fino a quando la fila è saltata, come le patatine su un commento.
· kamo-internal
Aggiornamenti filettati dal vivo e reazioni realmente raggiungibili
Due metà del filo sono stati costruiti e poi lasciati non collegati. Il server ha pubblicato ogni commento e evento di stato a `chat.session.{guid}` dal momento che la caratteristica è atterrata, e nulla abbonato — quindi la trascrizione si è spostata quando il lettore ha postato qualcosa di sé, e due persone triaging lo stesso rapporto ha visto diverse storie fino a quando uno di loro ha ricaricato. Ora abbona attraverso lo stesso contesto `TicketChat` utilizza, dopo il primo carico si è stabilito in modo da frame che arriva a metà fetch non è sovrascritto dalla pagina già in volo. Si rilegge la pagina piuttosto che appending la cornice, che è l'opposto di quello che chat fa e delibera: una chat corre a centinaia di messaggi, un thread di bug è un manciata, e il telaio non porta né la citazione di risposta né la lista di reazione — un riga aggiunta renderebbe senza la sua citazione fino a quando qualcosa altro ha costretto un ricarica. Le reazioni endpoint erano peggiori: server, servizio e campo DTO tutti spediti, e niente il cliente potrebbe raggiungere qualsiasi di esso. Non c'era traccia, nessun metodo api e niente disegnato. Ha tutti e tre ora, e `isReacted` viaggia come lo stato che viene chiesto per piuttosto che un delta, quindi un doppio clic racing converge invece di ribaltare due volte; il La risposta del server sostituisce l'elenco della riga piuttosto che unirsi in esso. Quattro emoji, offerto in linea. Un filo di bug non è una chat — le reazioni utili sono "Ho colpito anche questo", "guardarlo" — e un picker completo sarebbe più controllo che la conversazione ha bisogno di. I chip non utilizzati vengono estratti solo su hover del loro commento, quindi dieci commenti non sono quaranta pulsanti grigi. I test fingono il contesto websocket di proposito: il vero DEFAULT è un no-op che solo avverte, quindi un test senza un fornitore di portata sarebbe passato mentre il l'abbonamento era completamente assente.
· MediaService
La pagina del thread nomina il suo lettore, e cade un helper inutilizzato
`viewerMemberId` sulla pagina del thread, in modo che la console possa dire la propria reazioni da tutti gli altri senza una seconda chiamata a /api/user-info. Un ref riempito da una seconda fetch asincrona è vuoto per il primo render, e una reazione chip che inizia non riempito e si corregge un momento dopo si legge come un fallito clicca. Una stringa, per il motivo ogni id in questa funzione è una: un 19-digit int64 perde precisione attraverso JSON.parse. `none()` è andato — scritto come convenienza per il caso vuoto e mai chiamato.
· VOIPService
Realmente creare l'impostazione account Telnyx, portare MMS, e lasciare che un vettore solo org tenere filetti
Tre lacune, e la prima è stata la caratteristica di non funzionare affatto. LA DISPOSIZIONE DI TELNYX è stata DEFINED E CALATA DA NONHING TelnyxProvider.provision(), garantireMemberCredential() e E' tutto esistito e non aveva telefonate. Quindi... amministratore incollato una chiave API, salvato, è stato detto che il server del telefono è stato creato, e nulla è stato creato nel loro account Telnyx — dopo il quale fornitore segnalato voiceCalls=false e sms=false con un rimedio dicendo loro di premere Save, che non avrebbe fatto nulla per sempre. TelnyxSetupService ora funziona su creare e su ogni aggiornamento, idempotently, il modo La registrazione webhook di JustCall lo fa già. Scrive la connessione e file di messaggistica torna a configJson, motivo per cui è un servizio e non una linea nel provider: un fornitore è costruito DA che configura e non ha repository. Assegnare un numero ora mints che le credenziali SIP del membro e collega il numero al profilo di messaggistica — il passo manuale più gettato, e quello senza sintomo, perché un numero non collegato invia perfettamente e mai riceve. MMS Telnyx prende media urls, mai bytes. JustCall aveva già risolto esattamente questo con un link pubblico Redis-backed; è stato semplicemente chiamato per JustCall. Ora si estende un neutro OutboundMediaHandoff, quindi un fagiolo serve sia e nessun sito di chiamata JustCall o il test è cambiato. Entrambi i binari Telnyx trasportano i media, e ogni rifiuto è dichiarato — no MMS sul vettore, nessun handoff configurato, o un file l'editore rifiuta — quindi un membro non guarda mai un testo arrivare con l'immagine silenziosamente mancante. Un ORG CARRIER-ONly può ora avere una CONVERSAZIONE Questo è il limite del commit precedente documentato piuttosto che fisso. VOIP CONVERSATIONS.INSTANCE ID è nulla (kamo-shared-library ae95b01e), quindi un org con i numeri e un vettore e nessun server del telefono può filettare una risposta invece di L'ho fatto cadere. Due cose che hanno dovuto muoversi con esso: - Il dedupe. SQL tratta NULLs come DISTINCT, quindi l'indice unico esistente si ferma coprendo esattamente le righe che questo introduce. Un indice unico parziale (ORG ID, MEMBER ID, EXTERNAL PHONE NUMBER) Dove INSTANCE ID è NULL lo copre. - Due NPE in attesa del primo null. L'evento NATS è stato costruito con Map.of, che getta su un VALUE nullo — catturato dal gestore qui sotto e collegato a avvertire, in modo che la finestra SMS del membro semplicemente non aggiornerebbe mai. SmsController chiamato .toString() su di esso direttamente. DDL per entrambi è in doc/sql/ e applicato a mano; InitializerService non è distribuito in questo cluster.
· kamo-shared-library
Una conversazione di testo può esistere senza un server telefonico
VOIP CONVERSATIONS.INSTANCE ID non era NULL, che tranquillamente ha deciso che ogni filetto di testo appartiene a un PBX. Un'organizzazione che compra numeri e messaggi da un vettore e non risponde nessuna chiamata affatto — solo testo, nessun server del telefono ovunque — non ha VoipProviderInstance per un filo a cui appartenere. Quegli ordigni potrebbe SEND (un percorso di testo di un numero non ha bisogno di server telefonico) e aveva ogni risposta caduto, perché l'ingestione non poteva costruire una conversazione per archiviarlo sotto. Null è ora uno stato reale e il javadoc dice cosa significa, compresa la cosa non deve mai essere: un BulkTextProviderInstance id. Entrambe le tabelle sono UUID-keyed e consegnare un id alla fabbrica sbagliata fallisce come "nessuna tale istanza" piuttosto che fallire ad alta voce. Dedupe sopravvive al null, ma non gratis — SQL tratta NULLs come distinto, quindi il l'indice unico esistente si ferma coprendo queste righe. Il DDL accanto a VOIPService aggiunge un indice unico parziale su (ORG ID, MEMBER ID, EXTERNAL PHONE NUMBER) INSTANCE ID è NULL per esattamente quel caso.
· kamo-internal
Il piano che hai scelto viaggia alla pagina che lo vende
"Move to Business" ha aperto la console di fatturazione con nulla selezionato. La scelta della persona aveva appena fatto era caduto sul pavimento e sono stati invitati a farlo di nuovo, su uno schermo elencare quattro piani e aprire su nessuno — l'unico momento in questo viaggio in cui avevano già deciso. Il codice ora cavalca la stessa stringa di query del sub-tab, costruito e letto da planLinks.ts per il motivo per cui il sub-tab è: un valore non riconosciuto viene ignorato dal design, quindi un typo produce un link che funziona, atterra da qualche parte plausibile e non è dove ha detto che stava andando. Uno il modulo che possiede entrambe le metà è ciò che permette una cattura di prova che. I codici di piano sono confrontati con l'uguaglianza nei confronti della catalogo ovunque altro e un link non è posto per inventare una seconda ortografia. Non validato qui — questo modulo non sa quali piani esistono, quindi un segnalibro stante consegna il suo codice sopra e la console ignora ciò che non può corrispondere, piuttosto che errore a qualcuno che ha seguito un link che sono stati dati. Il lato della console sta atterrando separatamente, in un passaggio di sessione concomitante su quel file.
· kamo-shared-library
Le due lettere che un cliente ottiene quando la loro carta fallisce
BillingService ha voluto inviare questi da quando DunningNotifier è stato scritto. Si'. - un punto finale che non ha mai esisteva su EmailService, dove l'unico controller sotto quel prefisso serve directory Foto — e ingoiato il 404 per disegno, perché un guasto di posta deve non fare Stripe riprovare un webhook. Quindi un cliente la cui carta è scaduta ha un database fila e silenzio: nessuna lettera, nessuna bandiera, niente. Il primo che sapevano era applicazioni rifiutando di aprire una settimana dopo, quando il periodo di grazia è finito. Due modelli canonici, a seguito della costante/dotted-key dividono le HR e e-signature famiglie già utilizzano — fatturazione. pagamento failed e billing.service suspended, seme per-org come ogni altro. Entrambi vanno all'indirizzo BILLING sul conto piuttosto che a un membro, perché la persona che paga è di routine non una persona che si iscrive, e entrambi i link direttamente alla fattura ospitata da Stripe: qualcuno il cui accesso si è appena fermato non deve entrare nel prodotto per pagare per il prodotto. La formulazione fa il lavoro che le superfici non potrebbero. Il primo dice quello che è eccezionale, cosa si ferma, e il giorno esatto si ferma. Il secondo porta con "Niente è stato cancellato", perché questa è la paura in quel momento e non Lo schermo rispondeva. La metà del servizio di fatturazione — indicando DunningNotifier a /api/email/templates/send, che esiste ed è dietro InternalAuthFilter — atterra separatamente.
· kamo-internal
Una carta di piano dice che cosa aggiunge a quella sottostante
Il raccoglitore ha dato un prezzo, un tetto e una frase. Scegliere tra $46 e $80 a che è una decisione con una risposta ovvia, e per la maggior parte delle squadre è quello sbagliato — il catalogo ha portato tutta la matrice di capacità e la pagina di acquisto mai ha mostrato una linea. Ogni carta ora nomina ciò che il suo piano accende che quello precedente non, quattro a un tempo con un conteggio per il resto. Derivato, non scritto: planAddsOver legge lo stesso caratteristiche righe /myplan renders, quindi un cambiamento di catalogo si muove sia e non può andare stallo. Nomi solo — una caratteristica che il catalogo non ha chiamato è saltato piuttosto che indicato come SHARED MAILBOXES a qualcuno che non ha mai visto questo prodotto prima.
· kamo-internal
Un modo per raggiungere le vendite, e un controllo del sedile che conosce il limite del piano
ContattoSalesDialog detiene KamoCRM Dettagli dell'azienda — Sales@KamoCRM.com e (949) 298-9960 x1 — come costanti, deliberatamente. Ogni altro indirizzo di questa piattaforma appartiene all'inquilino, e risolvere questo lo stesso modo invierebbe un cliente chiedendo circa un Il piano KamoCRM torna alla linea di vendita della propria organizzazione. Entrambi sono collegamenti dal vivo e testo, con pulsanti di copia; il href del telefono trasporta l'estensione come ;ext=1 così un rubinetto lo compone. Un componente, utilizzato dalla pagina del piano e dalla console, perché un numero di telefono nessuno nota è stale è esattamente quello che due copie producono. Le carte di piano della console erano sbagliate in tre modi per la pagina che prende su nuovi clienti. Enterprise ha stampato una ditta $99 per sedile — il suo plan type è CUSTOM e kamocrm.com dice "Custom", così una società stava dicendo una prospettiva due cose diverse — e indossava "33% off applicato" distintivo sotto un prezzo che non ha avuto. Ora legge Prezzo su richiesta, non porta alcuna figura, e offre l'unica cosa che può effettivamente spostarla avanti: un conversazione. Starter e Business non hanno mai menzionato la tassa di avvio di $99-per-seat una volta, che è $495 su un ordine di cinque posti — l'intera prima fattura — citato come se non fosse nulla. Il gradino del sedile è andato a 9,999 qualsiasi piano è stato selezionato. Non e' vero. ha sempre applicato i soffitti reali, in modo che qualcuno potrebbe costruire un ordine di avviamento di quaranta posti, guardare un totale aggiungere per esso, e essere rifiutato a salvare con nulla indicando al numero che era sbagliato. Ora si ferma al limite del piano e dice quale piano l'ha impostato e cosa porta di più; è disabilitato fino a quando non viene scelto un piano, dal momento che il soffitto è una proprietà di tale scelta; e scegliere un piano più piccolo porta il conto giù con esso piuttosto che il prezzo cinque posti per dodici persone fino agli oggetti del server. morsettoSeatsToPlan è condiviso con il raccoglitore e testato, perché la stessa regola deve tenere quando i SEATS si muovono e quando il PLAN fa. Inoltre: il CTA primario era il blu predefinito di MUI — su una console white-label, l'unico colore su la pagina che appartiene a nessuno, sul singolo pulsante più importante.
· kamo-internal
Uno schermo Numbers, così chiamate e testi possono provenire da diverse aziende
Un'organizzazione acquista i suoi numeri da una società e risponde alle chiamate con un altro. Questo è normale, e Kamo non poteva descriverlo: il testo era una proprietà di un membro PHONE SERVER, quindi un org i cui DID e SMS sono venuti da Telnyx e le cui chiamate sono state esaudite dal proprio FreePBX non poteva affatto scrivere. Impostazioni → Telefono → Numeri è dove si dice ora, e si siede tra Fornitori e estensioni perché questo è l'ordine in cui il lavoro avviene. Una riga legge come una frase — "+1 (949) 555-1212 — Sarah Chen · chiama anello su Ufficio PBX · Testi passare attraverso Telnyx" — non una griglia di ids del fornitore. Il numero e la persona viene prima, perché queste sono le due cose che un amministratore In realta' lo sa. La decisione vale la pena di difendere, e l'unico NumbersTab.test. tsx esiste per proteggere: un server telefonico che non può testo è OFFERED nel menu a tendina dei testi, disabilitato, con il motivo accanto a esso. Filtrarlo fuori è l'attuazione evidente ed è quello che lascia qualcuno bloccato — un amministratore che non riesce a trovare il loro telefono sistema in quella lista conclude che lo schermo è rotto, mentre uno che legge "FreePBX non può inviare messaggi di testo" sa aggiungere un provider di testo. Le ragioni vengono dal server, dove ogni fornitore scrive il proprio. "Trova i miei numeri" importa quello che i fornitori sanno già, perché digitando trenta I DID correttamente non è una cosa da chiedere a nessuno e uno sbagliato non riesce silenziosamente. Telnyx si unisce a entrambi i raccoglitori. La sua carta passa la maggior parte delle sue parole sul webhook pubblico chiave: è su una pagina diversa del portale Telnyx dalla chiave API, e mancante non ha alcun sintomo sull'invio — i testi escono perfettamente e ogni risposta è rifiutata. La carta dice anche davanti a quello che Telnyx non ha, quindi questa è una decisione Qualcuno fa prima di commettere piuttosto che una scoperta dopo. Navi Kamo nessun logo Telnyx; una iniziale stagnata è onesta sull'essere nostro, dove qualcosa disegnato Sembrare il loro segno non sarebbe. Due cose trovate durante la costruzione: - Il reload di NumbersTab dipende da `t`, e l'usoTraduzioni non promette un identità stabile — così l'effetto ri-fuocato su ogni rendering e lo schermo mai carico finito. Non e' un errore, solo uno spinner permanente. Il test l'ha preso. - Il menu delle impostazioni e la mappa delle etichette del fornitore hanno entrambi i test di copertura che richiedeva manutenzione per una nuova scheda e un nuovo fornitore. Entrambi avevano ragione.
· VOIPService
Percorsi di NUMBER, e aggiungere Telnyx su entrambi i lati di esso
Un'organizzazione può acquistare i suoi DID e il suo testo da una società e rispondere chiama con un altro. Kamo non poteva esprimere che: il testo di un membro era un proprietà del loro PHONE SERVER, quindi un org i cui numeri provenivano da Telnyx e le cui chiamate sono state esaudite dal proprio FreePBX non poteva affatto testo — FreePBX dichiara sms(false), il gateway rifiutato prima che qualsiasi fornitore è stato raggiunto, e il consiglio "add a SMS provider" ha portato a una ferrovia che non poteva aprire un conversazione. Quindi la domanda si è spostata dal fornitore al numero. ORG PHONE NUMBER dice chi possiede un numero, cosa risponde alle sue chiamate e cosa porta i suoi testi. TEXT ROUTE KIND si trova accanto all'istanza, piuttosto che essere deferito da esso, perché i due tavoli candidati sono entrambi chiave UUID e consegnare un id alla fabbrica sbagliata non fallisce a gran voce — fallisce come "nessuna tale istanza" e il testo non viene mai inviato. InboundRail ha già documentato questo pericolo. TextRouter si risolve in tre livelli: il percorso del numero, quindi un predefinito chiamato dal server del telefono (così 200 DID su un PBX è un'impostazione, non 200 righe), quindi il server del telefono stesso — che è esattamente il comportamento di oggi, quindi un org che non apre mai la schermata Numbers continua a funzionare avendo configurato nulla. La rinfusa eredita finalmente il contratto di invio. TextSender ha due adattatori e SmsGateway ci parla, così BulkTextProvider.send Testo — il cui erroreMessaggio è sempre qualche vettore e.getMessage() — non può più mettere un SocketTimeoutException davanti a qualcuno che cerca di mandare un cliente. E BulkTextInboundController ora consegna i suoi messaggi a VoipMessageService. ingest invece di registrare una parola chiave e fermarsi, così una risposta su un numero di vettore diventa una conversazione. Parola chiave la movimentazione non si è spostata; si è spostata DOWN, nella sequenza ogni altra rotaia già condivide. VoipNumberOwnership guadagna SOURCE ORG NUMBER con la massima priorità: gli altri due fonti differiscono la proprietà da una colonna che capita di contenere un numero, mentre questo si legge una fila il cui scopo è quello di dire chi la possiede. Telnyx arriva sia come un server telefonico che come un vettore. Come server del telefono è primo onesto sul non essere un PBX: nessuna segreteria, nessun dispositivo, nessuna coda, nessun l'agente dichiara — e ogni di quelle bandiere false ora porta una frase un membro può leggere, perché una capacità che è falsa senza motivo scritto rende come una schermo vuoto dall'aspetto lavorativo. Questo è ciò che CapabilityLimit e PhoneCapability sono per; SMS ha avuto questo trattamento da smsUn availableReason(), e il resto anche la superficie. Le sue credenziali softphone sono coniate con le proprie ORG Chiave API, così a differenza di RingCentral nessuno ha a bordo ogni membro in un vettore portale. Due cose da sapere: - OrgPhoneNumberService prende PhoneProviderFactory @Lazy. Il ciclo è reale e legittimo, e Spring Boot ha proibito riferimenti circolari per impostazione predefinita da 2,6 — senza di essa il contesto non inizia, e nessun test unitario qui cattura che, perché tutti collegano i loro Docstors a mano. - Un org (numeri e un vettore, nessun server telefonico) può inviare ma non può thread a risposta: VOIP CONVERSATIONS.INSTANCE ID NON è NULL e parte del Indice di dedupe. Quella goccia è ora un ERROR che nomina il numero invece di un avvertimento. Il DDL di ORG PHONE NUMBER viene applicato a mano — doc/sql/. InitializerService non è distribuito in questo cluster.
· kamo-shared-library
Telnyx, il primo fornitore che è sia un server telefonico che un vettore
Telnyx vende SIP trunking, DID e messaggistica, e un'organizzazione può acquistare quelli è indipendente. Il caso è per: DID e testi acquistati da Telnyx, con le chiamate rispondete dal proprio FreePBX del cliente. Collassarlo in uno l'entrata costringerebbe quelli ad essere la stessa decisione, così TELNYX è allegata a entrambi VoipProviderTipo e BulkTextProviderTipo. Entrambi gli enum sono EnumType.ORDINAL, così entrambi gli append sono alla fine. Il testo di massa una aveva quella proprietà documentata nel suo javadoc e applicata da nulla; ora ha lo stesso test ordinale l'enum del telefono, ha affermato dove qualcuno aggiunge un il fornitore è già fatto per fermare e pensare. Telnyx legge THIRD PARTY: the org detiene il proprio account Telnyx e paga Telnyx per minuto e per messaggio, quindi ciò che Kamo legge è l'integrazione. An unassigned Telnyx linea non costa nulla, che è giusto — una telefonia credenziali con nessun membro dietro di esso è uno con cui non stiamo facendo nulla. Nessuna versione urto. 17 servizi pin 1.6.0 e ogni servizio Dockerfile ricostruisce questa libreria dal principale, quindi un urto lascerebbe tutti loro risolvere una versione che non esiste da nessuna parte. Recenti aggiunte a tipi / spediti allo stesso modo.
· kamo-internal
Lo scaffale sostituisce le carte per-app, e quattro correzioni alle sue file
Il launchpad era un muro di una carta per app. L'App Shelf elenca ogni di quelle applicazioni in un unico pannello, con ciò che ognuno fa e tutto dentro di esso, quindi la griglia di piastrelle stava dicendo gli stessi nomi due volte. Le carte sono sparite; ciò che è a sinistra sul launchpad è lo scaffale, i widget e le note pinned del membro. Ripassare un'app non piegata con loro — che era il controllo dell'angolo della carta, e il perno della mensola e' proprio ora. HomeCardPinControl e appPanes è andato con carte; LEAF TOOL KEYS era l'unica cosa in appPanes che li ha superati e ora siede in navRegistry, accanto alle opzioni che descrive. Quattro correzioni alla mensola stessa: - Il chevron arriva prima del perno, quindi ogni perno siede sulla stessa linea verticale se la sua fila si apre su qualcosa. Verificato a 8 righe, una x. - La metà aperta ora cresce: è un ITEM flessibile di fila, quindi il suo asse principale è la larghezza e il tratto solo mai raggiunto la sua altezza — ecco perché le sue file si fermò a breve della colonna che erano stati dati. - Un solo pulsante Open App è incentrato su entrambi gli assi, e dimensionato per la sua etichetta piuttosto che si estendeva sul pannello. La lista è 8px lead-in è caduto con esso, o è 8px di offset unilaterale che blocca il centraggio. - Quel pulsante legge ora "Open App".
· BillingService
Tassa di avvio di un piano, e l'offerta live, sul filo
PlanDto ha portato solo il tasso ricorrente, quindi ogni superficie citando un piano citato un prezzo il cliente non sarebbe addebitato: la tassa di avvio di KamoCRM è $99 un posto, che è $495 su un ordine di avviamento a cinque posti prima di un solo mese viene addebitato. La pagina dei prezzi pubblici ha sempre mostrarlo; le superfici in-app non avevano modo di farlo. Il piano payload porta anche ora l'offerta in esecuzione sul mercato, risolta dallo stesso predicare che in realtà sconti un ordine. Una pagina che cita il prezzo di listino accanto a un live 33%-off la promozione è sbagliata nel disprezzo del cliente e uno citando lo sconto dopo la sua decadenza è sbagliato nella nostra — né è un numero una pagina può decidere per sé.
· kamo-internal
Il filetto del rapporto nel cassetto, scaffalatura, e Problema Persisti
`SystemBugThread` rende tutto ciò che qualcuno ha detto su un rapporto e ogni stato che ha passato attraverso, sotto il callout corrente-status: il callout risponde "perché è qui ora", che è quello che la maggior parte dei lettori è venuto per, e la trascrizione risponde "come è arrivato qui", che è quello che il quarto tentativo di risolvere i bisogni. Si legge che la sessione mediatica si conclude. ogni altra trascrizione nella console utilizza. Coloro che autorizzano l'adesione alla sessione; un filetto di bug è leggibile da tutti che il RELAZIONE è visibile a, che è un lontano più ampio insieme delle persone che ci hanno inviato. Una finestra di dialogo ora raccoglie il commento per tutti e tre gli stati che portano uno. Loro differiscono nella formulazione e se il campo può essere lasciato vuoto, ed entrambi sono letti fuori dello stato — così shelving e Emissione Persists rifiutare un vuoto e una negazione ancora No. La voce segue l'abito: una negazione spiega una decisione, uno shelving spiega un ritardo, e una correzione fallita riporta ciò che è ancora rotto, e la voce tutti tre "Reason for denial" fecero l'ultimo letto come scusa. Problema Persists è il suo pulsante accanto a Fix Verified e il suo punto finale, disegnato da `canReportIssuePersists` — la risposta del server, mai derivata. Il suo pubblico è più ampio di Fix Verified e i suoi stati di partenza sono diversi, quindi un cliente rinviarlo da `canManage` o da proprietà lo nasconderebbe da esattamente collega per cui esiste. È disabilitato nel triage dropdown per lo stesso motivo: riaprire un rapporto ci sarebbe saltare il motivo per cui lo stato esiste per trasportare. `SystemBugDrawerTriage` ha guadagnato una correzione di ritaglio che vale la denominazione — il compositore del filo è un'area di testo sulla stessa pagina e viene prima in ordine di documento, e `[role="dialogo"]` non distinguono i due perché il cassetto porta anche quel ruolo. Un giroscopio lookup ha digitato la ragione di negazione nella casella di commento e ha inviato la negazione in bianco.
· EmailService
Variabili di anteprima per SYSTEM BUG COMMENT
Senza di loro l'editor di modelli rende la nuova lettera con buchi dove numero di rapporto, il mittente e il pulsante andare, che si legge come un modello rotto piuttosto che come anteprima mancante. Nessun commento corpo tra di loro, deliberatamente: la lettera non cita uno. A il filo del rapporto è leggibile da tutti il rapporto è visibile a, ma questa lettera è rivolto a una persona e in avanti come qualsiasi altra posta.
· InitializerService
System bug.media session guid e il suo indice di ricerca
La colonna dietro il commento di un report thread, corrispondente ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** si trova una colonna di stringa di 36 caratteri e un errore di tipo sulla unione si trova a tempo di query da chiunque sia sfortunato. Niente, e niente backfill. Vedere una sessione per relazione esistente significa scrivere quattro tavoli nell'ordine giusto con le coperture giuste, che appartiene al servizio che già sa come — `SystemBugService.ensureThread` lo fa sul primo tocco. A null qui significa "non ancora aperto", mai un errore.
· SecurityService
Invia all'IA dice a tutti sul filo, non solo il reporter
La spedizione invia la stessa lettera MediaService, quindi il suo pubblico deve muoversi allo stesso modo: ogni partecipante nel thread del rapporto meno chiunque abbia agito, ciascuno con il proprio saluto e il proprio fuso orario, piuttosto che il solo reporter. La passeggiata del partecipante è duplicata qui piuttosto che condivisa, e la duplicazione è deliberato — questo servizio non ha client MediaService, e l'aggiunta di uno così una spedizione può indirizzare una email metterebbe una chiamata HTTP sul percorso dello stato scrivere. The regola che ripete è due linee, e entrambi i lati passano attraverso lo stesso `SystemBugStatusEmail` predica.
· MediaService
Il filo dietro un rapporto, e i due stati che hanno bisogno di uno
`SystemBugThreadService` è l'intera funzione: MediaSession di un report, lo stato gli eventi scritti in esso con la ragione che il loro autore ha dato, i commenti chiunque che può vedere la relazione può lasciare, rispondere, reazioni e per-membro. Ogni punto di ingresso è SystemBugController, mai MediaController. Questo non è tidiness — gli endpoint dei media generici che autorizzano l'adesione alla sessione e un bug thread è leggibile da tutti che la RELAZIONE è visibile a, che è un set molto più ampio che le persone che ci hanno postato sopra. L'adesione significa "ha preso parte" e esiste per decidere a chi è scritto. Routing il thread attraverso il bug la regola della visibilità è ciò che mantiene quella regola in un posto che la possiede. I filetti sono creati pigramente e semi, non riempiti. I rapporti esistevano prima threads ha fatto, quindi `media session guid` è nullable e `ensureThread` lo riempie su primo tocco — scrivendo un evento CREATED timbrato con il tempo di creazione della fila, e spostando qualsiasi `denial reason` la riga porta ancora nella trascrizione prima Cancellare la colonna. Senza quella semina, sostituendo la colonna con un filo avrebbe silenziosamente gettato via l'unica frase per rapporto il vecchio disegno ha fatto riesce a mantenere. `thread()` è il writable letto che lo fa; `detail()` rimane sola lettura perché è il percorso caldo. `reportIssuePersists` è l'azione più ampia su un rapporto e l'unica capacità che non restringe lo spettatore: chiunque può vederlo può dire una correzione fallita, perché la persona che entra in una soluzione rotta è molto spesso non la persona che Ho presentato il rapporto. Si restringe invece di STATUS — una relazione che non ha mai affermato di essere fisso non ha nulla da contraddire. L'email di stato ora prevede una lettera per partecipante. `piani()` prende il il pubblico come parametro in modo che la regola rimanga provabile senza un contesto di primavera, e commenti gratuiti cavalcare ChatEmailNoticeService's ledger esistente sotto il loro propria chiave modello — una lettera per destinatario fino a leggere, digerito, muto rispettato.
· kamo-shared-library
Shelving, correzioni fallite, e l'intera cronologia dei commenti di un rapporto
Un rapporto ha mantenuto esattamente una frase. `system bug.denial reason` è stato sovrascritto da ogni movimento di stato successivo, così un bug che è stato negato, spiegato, riaperto, fisso e trovato ancora rotto finito con un motivo su quattro e nessun modo per leggere il Riposa. La correzione evidente è una tabella `system bug history` con una colonna di nota. Quel tavolo poi deve crescere filettatura, modificare la storia, le reazioni, gli allegati, per-membro e stato illeggibile — tutti i quali i tavoli multimediali dietro ogni chat e ogni biglietto di supporto ha già. Quindi un rapporto ottiene un MediaSession invece, sotto un nuovo tipo `SYSTEM BUG`, esattamente come `SupportTicket` già fa: cambia stato sono scritti come messaggi di sistema che portano la ragione che il loro autore ha dato, commenti come quelli normali. Ci sono due stati. SHELVED è parcheggiato-non-rifiuto, che `WONT FIX` potrebbe non dire — uno è una posizione di coda e l'altro è una decisione, e un reporter ha detto "abbiamo deciso di non farlo" sul lavoro che è solo in attesa fermate di archiviazione rapporti. ISSUE PERSISTS è il suo contrappeso: una soluzione spedita che non ha tenuto. Entrambi richiedono una ragione; una negazione deliberatamente ancora non lo fa, perché costringere uno lì solo insegna alle persone a digitare una fermata completa. L'adesione a un thread di bug non significa cosa significa in una chat. Il filo è leggibile da tutti il RELAZIONE è visibile a; `MediaSessionMember` righe significa "ha preso parte", e decidere chi è e-mail e chi ha muto. Ecco perché niente qui viene servita tramite MediaController, la cui regola di accesso è l'appartenenza. Il pubblico della lettera di stato segue: ogni partecipante meno l'attore, piuttosto che il giornalista da solo. Il vecchio "il reporter lo ha spostato, quindi non mandare nulla" veto avrebbe fatto Problema Persisti avvisare nessuno affatto — almeno tutto quello che ha scritto la soluzione che ha appena fallito.
· kamo-internal
Uno schema del documento e uno scheletro nella forma della pagina
Solo l'eroe era una vera voce. Tutto il resto — le nove categorie del libro, pannelli di conto, il patto, ogni opzione di aggiornamento — era un div stilizzato, quindi un screen-reader utente ha avuto un punto di riferimento nella pagina più lunga nell'area impostazioni e non c'è modo di muoversi tra categorie. Sono h2 e h3 ora, e il libro principale segue il modello di fisarmonica ARIA con la voce WRAPPING il suo gioco: nidificato l'altro modo in cui la categoria è annunciata come parte di il controllo e smette di essere da qualche parte si può saltare a. Lo stato di carico era un spinner centrale, che è lo strumento sbagliato qui. Una richiesta riempie l'intero schermo, così il girante si sedeva nel mezzo del nulla e il layout finito poi arrivò subito e spostato tutto sotto il lettore. Lo scheletro tiene il reale proporzioni — la banda dell'eroe con la sua rotaia, la colonna lunga del libro principale, la colonna più corta di pannelli accanto a esso — così l'arrivo è un riempimento piuttosto che un salto. Non c'è movimento proprio. oltre l'onda di MUI; la pagina ha esattamente un'animazione deliberata e non dovrebbe dover competere con quello che era sullo schermo un momento prima. I test indicano il contorno e la nidificazione della fisarmonica, più due cose che il libro principale ottiene a destra e si ferma silenziosamente a destra: il catalogo "100.0000" reso 100, e un caratteristica booleana mai descritta come Unlimited.
· kamo-internal
Disegnare il dio governa il server ora detiene
La scheda Utenti ha bloccato ogni riga dio per tutti, ed entrambi hanno sbagliato. Troppo sciolto, perché la serratura viveva solo in questo componente — gli endpoint dietro ha risposto a una chiamata diretta da chiunque tiene MANAGE USERS. Troppo stretto, perché ha anche bloccato l'unico chiamante diritto alla modifica. GodAccountGuard è il autorità ora, e il cassetto legge callerIsGod dalla stessa risposta del fila: bloccato per tutti gli altri, vivere per un pari, e detto ad alta voce nel avvertire il tono quando è vivo, perché una riga dio modificabile sembra ordinario. La bandiera sintetica era l'unico controllo che rimase vivo su una fila dio, ed è il più pesante sullo schermo — ferma l'account autenticando affatto. Si'. si muove sulla stessa porta del resto. Impersonate viene ritirato su un bersaglio di Dio. Il server lo rifiuta su entrambe le porte, e un pulsante che viene offerto e poi rifiuta legge come un difetto piuttosto che come la regola che è. accountIsGod cavalca dal profilo il modo in cui membroTipo già lo fa, e il server dice solo un lettore dio-eleggibile.
· SecurityService
Solo un dio può amministrare un dio, e nessun dio è impersonabile
MANAGE USERS ha raggiunto l'identità di vetro. UserAdminController sorvegliato solo l'Utente di Sistema, in modo che un titolare non-di-dio potrebbe disabilitare un account dio e revocare le sessioni dal vivo, riscrivere i suoi identificativi firmati, flag it sintetica, o inviare una password reset per esso — ognuno di loro un modo per disarmare la gente che altrimenti lo avrebbe annullato. La console già disegnava la fila come intoccabile e detto così in un commento; niente dietro lo schermo concordato. GodAccountGuard applica la regola di peerage PlatformAdministratorController già tenuto sopra la piattaforma di un dio DIRITTI sul conto che quei diritti pendono. L'account era il più debole delle due porte: prendere la password di qualcuno è una superset di prendere i loro diritti. Protegge tutti e cinque i percorsi di scrittura, e braccio PLATFORM ADMIN di UserAccountEditPolicy, che è l'altro diritto dello stesso superficie — l'aggiornamento dell'identità del profilo del membro e sia l'indirizzo personale endpoints. Continua a leggere, così la console può spiegare il rifiuto. L'obiettivo non è chiuso e il chiamante non si apre, deliberatamente: una fila che non si può leggere è Dio, una fila che non esiste non è, e un chiamante che non può essere risolto non è. Sollevare sempre una restrizione è quello che decide in che modo ogni lato va. Impersonation ottiene lo stesso obiettivo rifiutato, senza pari affatto. The La porta cross-org ha sempre rifiutato un dio. la porta dello stesso-org dietro le impostazioni pagina mai fatto, che lo ha reso il più economico dei due modi in cui. Impersonare un dio porta il chiamante l'ultima piattaforma di nuovo, così ammette nessuna eccezione — nemmeno per un altro dio. Le risposte ora portano il callerIsGod, e il payload del membro porta accountIsGod per un lettore divini. Senza di loro la console deve bloccare ogni riga dio per tutti, compreso l'unico chiamante autorizzato alla modifica.
· kamo-internal
Il personale può scrivere la frase che il cliente legge
La nota visibile dal cliente aveva una colonna, un campo DTO e una pagina di piano pronta a renderla, e Non c'è modo per nessuno di impostarlo - così Harmony è stato l'unico che mai esisterebbe, e solo perché era seme a mano. Si tratta di una seconda scatola piuttosto che di un "mostra questo al cliente" interruttore sulla nota interna. Un interruttore invita a riutilizzare una frase scritta per colleghi, e la nota interna è dove il personale registra ciò che è stato concesso e a chi; può rimanere candido solo se pubblicare qualcosa è un atto separato di scrittura. Il vuoto rimane la risposta normale — un accordo senza cliente nota ancora rende in pieno dai suoi termini strutturati. Entrambe le note sono ora etichettate nella lista, perché la differenza tra loro è l'intero punto e un secondo comma non chiarito si legge come più del primo.
· kamo-internal
Il softphone ottiene una casella di posta, e una finestra SMS è un thread
La finestra dello strumento SMS si è aperta su un dialpad di dodici chiavi e poi è diventato un tre-tab finestra -- Chat / Templates / CRM -- intitolato "Conversazione #a3f1c8de-...". Nulla è così che chiunque utilizza messaggi di testo, e due delle tre schede non erano reali: SmsCrmTab era DUMMY CONTACT e DUMMY ACTIVITY ("John Smith, Acme Corp", hardcoded, mostrato ad ogni membro su ogni cliente) mentre un inverso autentico ricerca già esisteva in SoftphoneCRM, e SmsTemplatesTab leggere un cinque-item costante mentre SmsTemplateController di VOIPService non è stato chiamato da nulla. Non c'era nemmeno un posto per vedere i tuoi testi: la finestra mostrava una conversazione e non poteva raggiungere un altro, quindi le uniche rotte per un filo erano un in arrivo notifica che spunta una finestra a voi, o il Centro Interazione. La finestra - Due stati, nessuna scheda: un raccoglitore destinatario, o un thread. Modelli spostati al pulsante bullone del compositore sulla vera API; dettaglio di contatto per le informazioni dell'intestazione rendering pannello SoftphoneCRM. SmsCrmTab, SmsTemplatesTab, smsTemplateData e SmsNewConversation vengono eliminati. - Compose è destinatario-primo e non ha una casella di messaggio. Questo è ciò che rende cassaforte: una finestra che chiude a favore di una già aperta chiude vuoto per costruzione, quindi "una finestra degli strumenti non deve mai perdere quello che un membro digitato" tiene senza alcun trattamento attento. - Il pannello di contatto si scambia con `display`, mai un rendering condizionale -- che distruggerebbe il compositore ogni volta che qualcuno lo guardò, che è il fallimento il vecchio tab bar continuava a produrre. Una conversazione, una finestra Il negozio di attrezzi è sempre dedupato su oggetti di scena. conversazioneId -- un-minimize, Alzati, bagliore. Non poteva vedere una finestra SMS aperta da PHONE NUMBER, perché SmsTool ha risolto il id in stato locale e non ha mai detto al negozio, quindi l'apertura quella conversazione da qualsiasi altra parte ha costruito una seconda finestra sulla parte superiore del primo. SmsTool ora patch l'id sui propri oggetti (come fa ChatTool per il suo sessionGuid) e titoli la finestra con il contatto. Scegliere un destinatario stabilisce tre modi: creare qui, adottare qui, o alzare la finestra che lo ha e chiudi questo. Il softphone Una quinta scheda TEXTS, composta da ciò che già esisteva per Interaction Center -- useConversationRows' secchio testo, ConversationRow, unreadForRow, openConversation -- plus search and a new-message button. The badge legge UnreadContext.bySurface.sms, già vivere sopra la presa, quindi Non costa niente. Nomi di contatto ConversationRow ha ammesso "non c'è una ricerca inversa lato server da un numero a un nome di contatto, quindi una riga di testo è intitolata dal suo numero". Nuovo ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** contatti da canonicalPhone, nel browser perché la ricerca del destinatario carica comunque. Un indice ridefinisce l'intestazione, l'elenco TEXTS e il navigatore. Fisso anche: SmsNotificationListener timbrato ogni telaio di presa INBOUND e ignorato la direzione che l'evento ha sempre portato, quindi un membro di uscita testo è tornato reso come il cliente e ha alzato una finestra per qualcosa che avevano appena fatto. La finestra ora porta l'intera superficie di testo su GET /sms/capability invece di permettere a un team o a un org FreePBX di scoprirlo premendo inviare in silenzio, e un invio rifiutato mostra la propria frase del fornitore con il messaggio contrassegnato Non consegnato. Richiede il commit di voipservice che aggiunge /sms/capability e il risultato dell'invio.
· kamo-internal
Una pillola Free Plan e una pagina a /myplan dietro entrambe le pillole
La pillola di prova ha vissuto due volte — la ferrovia nav e il lanciopad — con il conto alla rovescia, il cancello di tipo membro e lo stile copiato tra loro. Entrambi hanno fatto il processo in diretta, quindi... organizzazione il cui processo era appena finito non ha visto nulla affatto: nessuna pillola, nessuna spiegazione, nessuna segno che il prodotto era diventato più piccolo. E nessuna pillola è andata da nessuna parte, quindi un membro che capì che il conto alla rovescia non aveva un passo successivo. Ora entrambi gli stati sono definiti una volta, in PlanPill, che mantiene le due forme della ferrovia e il terzo launchpad mentre decide il MEANING in un unico posto. Mostra ad ogni membro del team e LINK solo per qualcuno che può agire sul piano; per tutti gli altri il tooltip è il l'intera caratteristica, motivo per cui ogni stato ha un nome gemello di sola lettura che per chiedere invece. /myplan risponde dove l'organizzazione sta, quanto tempo ha, che cosa può usare, che cosa un il piano più grande aggiunge, e i termini che ha negoziato. Tre cose che vale la pena conservare: Il termine ferroviario è l'eroe, ed è un dispositivo con tre significati — una prova drenante a la sua scadenza, un piano pagato che passa attraverso il suo periodo di fatturazione, un processo scaduto alla sua fine. È posto alla sua vera larghezza e animato con un keyframe piuttosto che dallo stato, quindi un script lento o un'idratazione fallita mostra ancora la figura reale invece di una barra vuota. Il registro stampa i valori, non le zecche. Il catalogo contiene numeri reali — 100 contatti, due condutture, 4 GB a sedile, illimitato — e quelli sono ciò che un proprietario decide su. Escluso righe rimangono, deboli e ordinati di seguito: sono l'argomento per l'aggiornamento, e nasconderli farebbe sembrare il piano libero completo. Gli aggiornamenti sono una differenza, non una griglia di prezzo. Il lettore ha già un piano; la loro domanda è quello che ottengono che non hanno. Ordinato per prezzo — ordinando da quante righe un piano luci in su sembra equivalente e non è, dal momento che un piano sovrapposto più grande può aggiungere meno. L'acquisto avviene ancora in BillingConsole, che questo deep-links in sub-tab. Ricostruzione il checkout qui sarebbe una seconda implementazione dell'unica cosa che non deve avere due. La tavolozza segue la convenzione di casa — una rampa --plan-* mescolata da --colore-primario — con un'eccezione: i colori dello stato sono fissi, perché un'organizzazione il cui marchio è ambra altrimenti indossare un distintivo di prova nel suo colore pagato, e l'unica cosa che questa pagina esiste dire sarebbe la cosa che non poteva dire.
· BillingService
Un punto finale dietro la pagina del piano del cliente
GET /api/billing/self/plan risponde a tutto /myplan rende: il piano, che cosa il l'organizzazione può effettivamente utilizzare, il catalogo per confrontare, sedili e il loro roster, stoccaggio, rinnovo e qualsiasi accordo negoziato. Una richiesta piuttosto che sei, per una ragione al di là degli stati di carico: il livello di accesso non può onestamente essere deciso nel browser. Decidere significa elencare il conto membri, e un membro può elencarli solo se già tengono l'accesso è deciso. Quindi... è deciso qui, due volte — il cancello del cliente decide che cosa a OFFER, questo decide che cosa può essere leggere — e un chiamante SELF PAY riceve un carico utile del roster del sedile, totale dell'organizzazione e l'accordo non è mai stato messo in. Mantenere i battiti nascosti: qualcuno che legge la scheda di rete una pagina che sono autorizzati ad aprire dovrebbe imparare nulla che non è stato permesso di essere detto. Il processo e' la trappola che vale la pena nominare. Un abbonamento di prova si trova su plan code = GRATIS, perché TrialAdminService converte la riga di default gratuita in atto, mentre EntitlementService concede l'intero catalogo illimitato. Servire le righe di funzionalità del piano durante un processo dire a un cliente che non hanno AI, e-signatures o SSO mentre sono in mezzo utilizzare tutti e tre — nella settimana stanno decidendo se acquistare. Quindi un rapporto di prova attivo l'unione del catalogo come incluso. Un processo PENDING deliberatamente non lo fa: il suo orologio non ha iniziato, e promettendo altrimenti è una bugia il prodotto contraddice al primo clic. L'accordo è restituito derivato, mai trascritto. La prosa è clienteVisibleNote e solo questo; le note interne non lasciano mai il servizio. E il costo del sedile successivo è un numero da SetupFees.forAdded Sedili — lo stesso codice che solleva la carica — piuttosto che un frase che descrive la regola, perché una frase è una seconda attuazione che sarà alla fine non sono d'accordo con il primo.
· SecurityService
Di' su quale piano e' un'organizzazione e chi puo' cambiarla
La console poteva dire che una prova era in esecuzione e nient'altro. il diritto "TRIAL" o null, così libero, scaduto-trial, pagato e le organizzazioni comped erano tutte le stesse valore — ecco perché un'organizzazione il cui processo era appena finito non ha visto pillola, no spiegazione, e nessun segno che il prodotto era diventato più piccolo. planMode nomina lo stato e il pianoAccessTier nomi che possono agire su di esso. Entrambi cavalcano diritto payload ogni pagina già carica, quindi le pillole che le leggono non costano più richiesta su qualsiasi percorso — che importa, perché la ferrovia nav rende su tutti loro. il dirittoSource è deliberatamente intoccato. Ha due valori oggi ed è letto da due Pillole in piu', pre-autenticita'. dipinge gli schermi di login e di registro; allargamento cambierebbe quello che tutti e tre significano a una volta. La prevalenza è PAID > COMP > TRIAL ACTIVE > TRIAL PENDING > TRIAL EXPIRED > GRATIS sottoscrizioni filtrate dalla regola condivisa èLive — la stessa di BillingService EntitlementModeResolver utilizza, quindi i due non possono essere in disaccordo su un account PAST DUE all'interno il suo periodo di grazia. È portante di carico: una riga FREE DEFAULT è semenzata alla registrazione e non è mai rimosso quando un piano viene acquistato, quindi "la prima riga trovata" riferisce GRATIS per la maggior parte paga clienti. Il livello è risolto qui piuttosto che nel browser perché il browser non può risolverlo onestamente: decidere da file di account-membership significa elencare i membri, che già richiede che l'accesso sia deciso. Un delegato conta solo su un finanziamento Account QUESTO organizzazione — il personale della piattaforma amministra regolarmente account targeting altrove.

Il tuo universo ti sta aspettando.
Aggiungi i prodotti quando sei pronto: nessuna migrazione, nessun nuovo login, nessuna richiesta di pagamento.
Gratis per sempre — nessuna carta di credito richiesta