Changelog
O que há de novo.
Cada melhoria para a plataforma, como ele envia.
· VOIPService
Serve o URL do webhook inbound, a partir de uma definição
Um administrador que adiciona um provedor de texto tem que colar o URL de entrada do Kamo no A consola do porta-aviões, e nada lhes disse o que era. Faltando esse passo tem nenhum sintoma no lado ninguém testa primeiro: mensagens enviar perfeitamente e cada a resposta é retirada. Está bem. Servido em vez de codificado no navegador pelo motivo do URI de redirecionamento da RingCentral ser servido: o valor vem de uma propriedade de implantação, e uma cópia no frontend mantém confiante mostrando a antiga máquina após o real se mover. A URL agora tem uma definição. TelnyxProvisioner WRITES-lo no cliente perfil de mensagens e a tela de configurações mostra a alguém colando-o mão; quando essas eram duas cordas nada teria falhado se eles derivaram — um administrador teria simplesmente sido instruído para configurar um URL nada Ouve. InboundWebhookUrls é essa definição, e seu teste afirma que cada lesma que constrói resolve de volta através InboundSmsNormalizer, então os dois lados concordam por construção em vez de por uma tabela alguém deve se lembrar de atualizar. O ponto final também relata se a entrada de uma transportadora está genuinamente ligada — a verificação de assinatura E um analisador. Entregando uma URL para uma operadora cujas cargas nada pode ler seria uma instrução para configurar algo que então se recusa cada mensagem, que é pior do que dizer claramente que ainda não é apoiada.
· kamo-internal
Uma configuração que cobre cada número em um sistema de telefone
O projeto de roteamento aprovado tinha três níveis e apenas dois deles eram alcançáveis. Um número poderia nomear seu próprio provedor de texto, e falhando que o servidor de telefone era pediu para texto em si — mas o nível médio, "a menos que um número diz o contrário, o nosso textos passam por Telnyx", foi implementado em TextRouter e setable nowhere. An organização com duzentas DIDs em um FreePBX teve que editar duzentas linhas para expressar um fato, que é o trabalho de entrada de dados na tela Números era suposto evitar. Agora é um dropdown no próprio servidor de telefone. Três coisas que valem a pena nomear: - A mesclagem executa LAST em save(). Cada filial de provedor acima dele ASSINA configJson ao invés de fundir com ele, então a primeira versão deste foi silenciosamente deixado em servidores Telnyx — o único provedor cujo branch acontece vir depois de onde originalmente se sentou. - "Nada - definir cada número individualmente" em vez de uma opção em branco, porque um dropdown vazio lê como desactivado em vez de como uma decisão. - A chave é uma string em duas línguas em dois repositórios, e deriva é SILENT: a configuração salva, o dropdown ainda mostra o provedor que foi escolhido, e cada número que depende do recuo silenciosamente cai para SMS através do servidor de telefone — o que para o FreePBX significa não enviar mensagens. Então... o formulário importa DEFAULT TEXT INSTANCE KEY, e o textoRouteContract.test.ts pinos ele contra TextRouter.java a maneira de BillingCycles.test.ts prende o billing enum. Esse guarda é deliberadamente honesto sobre o que prova. O meio repo cruz salta sempre que o checkout do irmão estiver ausente ou estagnado — que é na maior parte do tempo — por isso é nomeado "quando essa fonte está disponível" e um passe significa "não contradizidos", nunca "verificados". A asserção que sempre corre é aquela que Apanharam um nome quando o guarda foi sondado, quebrando-o de propósito. Documento de projeto atualizado: os dois limites que ele listou como diferido – uma transportadora somente organização incapaz de colocar uma resposta, e MMS através de Telnyx — ambos estão fechados em voipservice 8eaebfb.
· kamo-internal
Reagir a uma mudança de estado, não apenas a um comentário
"" em um evento fixo é a única coisa mais natural que qualquer um quer dizer em um thread de bug, e oferecer reações apenas em comentários significava digitar um para dizer isso. Centrado sob a pílula evento, e escondido até que a linha é pairado, como as fichas sobre um comentário.
· kamo-internal
Atualizações de thread ao vivo e reações que são realmente alcançáveis
Duas metades do fio foram construídas e depois deixadas sem ligação. O servidor publicou todos os comentários e eventos de status para `chat.session.{guid}` desde que o recurso desembarcou, e nada subscrito — então a transcrição só se moveu quando o leitor postou algo em si, e duas pessoas triando o mesmo relatório viu diferentes histórias até que um deles recarregado. Agora assina através do mesmo contexto `TicketChat' usa, depois que a primeira carga se estabeleceu assim que um O quadro que chega no meio da busca não é substituído pela página já em voo. Ele re-ler a página em vez de anexar o quadro, que é o oposto do que chat faz e delibera: um chat corre para centenas de mensagens, um tópico de bug é um e o quadro não carrega nem a citação de resposta nem a lista de reações — uma a linha anexada renderizaria sem sua citação até que algo mais forçasse uma recarga. O endpoint de reações foi pior: servidor, serviço e campo DTO todos enviados, e não O cliente pode chegar a qualquer coisa. Não havia nenhuma rota, nenhum método api e nada desenhado. Ele tem todos os três agora, e `estáReagido' viaja como o estado que está sendo solicitado por do que um delta, então um duplo-clique de corrida converge em vez de virar duas vezes; o A resposta do servidor substitui a lista da linha em vez de se fundir nela. Quatro emojis, oferecidos em linha. Um tópico de bug não é um bate- papo — as reações úteis são "Eu bato isso também", "olhando para ele" - e um selecionador completo seria mais controle do que A conversa tem utilidades. Os chips não usados são desenhados apenas sobre o hover dos seus comentário, então dez comentários não são quarenta botões cinzentos. Os testes simulam o contexto websocket de propósito: o DEFAULT do real é um no-op que apenas adverte, de modo que um teste sem um provedor em escopo teria passado enquanto o A assinatura estava totalmente ausente.
· MediaService
A página de thread nomeia seu leitor, e deixa cair um ajudante não utilizado
`viewerMemberId` na página do tópico, para que o console possa dizer o próprio visualizador reações de todos os outros sem uma segunda chamada para /api/user-info. Um árbitro preenchido por uma segunda busca assync está vazio para a primeira renderização, e uma reação o chip que começa não preenchido e corrige-se um momento depois lê como um fracasso Clique. Uma string, pela razão que cada ID neste recurso é um: um int64 de 19 dígitos perde precisão através de JSON.parse. `none()` se foi — escrito como uma conveniência para o caso vazio e nunca ligou.
· VOIPService
Na verdade, crie a configuração da conta Telnyx, carregue MMS e deixe que uma org-somente portadora mantenha threads
Três lacunas, e a primeira foi a característica não funcionar em tudo. TELNYX PROVISIONING FOI DEFINIDO E CHAMADO POR NADA TelnyxProvider.provision(), assegurarMemberCredential() e Todos existiam e não tinham chamadas. Então... administrador colou uma chave API, salvo, foi dito que o servidor de telefone foi criado, e nada foi criado em sua conta Telnyx - depois do que o provedor relatou vozCalls=false e sms=false com um remédio dizendo-lhes para Pressione Save, que não teria feito nada para sempre. TelnyxSetupService agora é executado em criar e em cada atualização, de forma idempotente, o caminho O registo do JustCall já faz. Ele escreve a conexão e messaging-profile ids back into configJson, por isso é um serviço e não uma linha no provedor: um provedor é construído a partir dessa configuração e não tem repositório. Atribuir um número agora menta a credencial SIP desse membro e atribui o número ao perfil de mensagens — o passo manual mais perdido, e aquele sem sintoma, porque um número não unido envia perfeitamente e nunca recebe. MMS Telnyx leva media urls, nunca bytes. JustCall já tinha resolvido exatamente isso com um link público apoiado por Redis; foi simplesmente nomeado para JustCall. Agora se estende um OutboundMediaHandoff neutro, então um bean serve tanto e nenhum site de chamada JustCall ou o teste foi alterado. Ambos os trilhos de Telnyx transportam meios de comunicação social, e cada recusa é declarada — não MMS na operadora, nenhuma transferência configurada, ou um arquivo que o editor rejeita — assim um membro nunca vê um texto chegar com o quadro silenciosamente faltando. UMA ORG CARRIER-ONly pode agora manter uma conversa Este é o limite do commit anterior documentado em vez de corrigido. VOIP CONVERSATIONS.INSTÂNCE ID é nulável (kamo-shared-library ae95b01e), então um org com números e uma operadora e nenhum servidor de telefone pode Deixá-la cair. Duas coisas que tiveram de mudar com ela: - O dedupe. SQL trata NULLs como DISTINCT, então o índice único existente pára cobrindo exatamente as linhas que isso introduz. Um índice único parcial sobre (ORG ID, MEMBRO ID, EXTERNA PHONE NUMBER) - Dois NPEs à espera do primeiro nulo. O evento NATS foi construído com Map.of, que lança um valor nulo — capturado pelo manipulador em baixo e registado em alerta, assim a janela SMS do membro simplesmente nunca atualizaria. SmsController chamado .toString() nele diretamente. DDL para ambos está em docs/sql/ e aplicado à mão; InicializerService não está implantado neste cluster.
· kamo-shared-library
Uma conversa de texto pode existir sem um servidor de telefone
VOIP CONVERSAÇÕES.INSTÂNCIA ID NÃO era NULL, que calmamente decidiu que cada o tópico de texto pertence a um PBX. Uma organização que compra números e mensagens a partir de uma operadora e não responde a chamadas em tudo — apenas SMS, nenhum servidor de telefone em qualquer lugar — não tem VoipProviderInstância para um fio pertencer. Essas orgias poderia SEND (uma rota de texto de um número não precisa de servidor de telefone) e tinha todas as respostas caiu, porque a ingestão não pôde construir uma conversa para arquivá-la. Null é agora um estado real e o javadoc diz o que significa, incluindo a coisa nunca deve ser: um BulkTextProviderInstance id. Ambas as tabelas são UUID-keyed e entregando um id para a fábrica errada falha como "não tal instância" em vez de falhando alto. Dedupe sobrevive ao nulo, mas não de graça — SQL trata NULLs como distintos, então o O índice único existente pára de cobrir estas linhas. A DDL ao lado do VOIPService adiciona uma índice único parcial em (ORG ID, MEMBRO ID, EXTERNA PHONE NUMER) INSTANCE ID é NULL para exatamente esse caso.
· kamo-internal
O plano que você escolheu viaja para a página que o vende
"Move to Business" abriu o console de faturamento sem nada selecionado. A escolha da pessoa tinha acabado de fazer foi deixado cair no chão e eles foram convidados para fazê-lo novamente, em uma tela listando quatro planos e abrindo em nenhum — o momento em que tiveram Já decidi. O código agora monta o mesmo texto de consulta que o sub-tab, construído e lido por planLinks.ts para a razão pela qual o sub-tab é: um valor não reconhecido é ignorado pelo design, link que funciona, pousa em algum lugar plausível e não é onde ele disse que ia. Uma módulo que possui ambas as metades é o que permite que um teste pegar isso. Os códigos de plano são comparados pela igualdade em relação ao catálogo em qualquer outro lugar e um link não é lugar para inventar uma segunda ortografia. Não validado aqui — este módulo não sabe quais planos existem, então um marcador velho entrega seu código sobre e o console ignora o que ele não pode corresponder, em vez de errar em alguém que seguiu uma ligação que lhes foi dada. O lado do console está pousando separadamente, em um passe de sessão concorrente sobre esse arquivo.
· kamo-shared-library
As duas letras que um cliente recebe quando seu cartão falha
BillingService quer enviar estes desde que DunningNotifier foi escrito. Ele afixado para ************* - um ponto final que nunca existiu no EmailService, onde o único controlador sob esse prefixo serve fotos de diretório — e engoliu o 404 por design, porque uma falha de correio deve não fazer o Stripe tentar novamente um webhook. Então um cliente cujo cartão expirou tem uma base de dados remar e silêncio: nenhuma carta, nenhuma bandeira, nada. A primeira vez que souberam disso foi aplicativos se recusando a abrir uma semana depois, quando o período de graça acabou. Dois modelos canónicos, seguindo a chave constante/dotada dividida pelo RH e As famílias de assinatura electrónica já utilizam — facturação. payment failed e billing.service suspended, semeado per-org como todos os outros. Ambos vão para o endereço BILLING na conta em vez de para um membro, porque a pessoa que paga não é rotineiramente uma pessoa que assina, e ambos direto para a fatura hospedada da Stripe: alguém cujo acesso acabou de pausar não deve ter que entrar no produto para pagar pelo produto. O texto faz o trabalho que as superfícies não puderam. O primeiro diz o que é excelente, o que para, e no dia exato que para. A segunda leva com "Nada foi apagado", porque esse é o medo naquele momento e não O ecrã estava a atender. A metade BillingService — apontando DunningNotifier para /api/email/templates/enviar, que existe e está por trás InternalAuthFilter — terras separadamente.
· kamo-internal
Um cartão de plano diz o que ele adiciona ao abaixo dele
O catador deu um preço, um teto de assento e uma frase. Escolhendo entre $46 e $80 a assento nisso é uma decisão com uma resposta óbvia, e para a maioria das equipes é a errada - o catálogo carregou toda a matriz de capacidade o tempo todo e a página de compra nunca mostrou uma linha dele. Cada carta agora nomeia o que seu plano liga em que o anterior não, quatro em um tempo com uma contagem para o resto. Derivado, não escrito: planAddsOver lê o mesmo linhas do recurso /myplan renderiza, então uma mudança de catálogo move-se tanto e nenhum pode ficar velho. Apenas nomes — uma funcionalidade que o catálogo não nomeou é ignorada em vez de mostrada como SHARED MAILBOXES para alguém que nunca viu este produto antes.
· kamo-internal
Uma maneira de chegar às vendas, e um controle de assento que sabe o limite do plano
ContatoVendasDialog detém KamoCRM Dados próprios da Inc. — Sales@KamoCRM.com e (949) 298-9960 x1 — como constantes, deliberadamente. Cada outro endereço nesta plataforma pertence para o inquilino, e resolver este da mesma forma enviaria um cliente perguntando sobre um KamoCRM planeja voltar à linha de vendas de sua própria organização. Ambos são links ao vivo, bem como texto, com botões de cópia; o telefone href carrega a extensão como ;ext=1 assim que um toque disca-lo. Um componente, usado pela página do plano e do console, porque um número de telefone ninguém percebe É exatamente o que duas cópias produzem. Os cartões de plano do console estavam errados em três maneiras para a página que assume novos clientes. Empresa imprimiu uma empresa $99 por assento — seu tipo plan é CUSTOM e kamocrm.com diz "Alfândega", então uma empresa estava dizendo a uma perspectiva duas coisas diferentes — e ele usava um "33% de desconto aplicado" crachá sob um preço que não tinha. Agora lê-se Priced na aplicação, não carrega nenhuma figura, e oferece a única coisa que realmente pode movê-lo para a frente: a Conversa. O Starter e o Business nunca mencionaram a taxa de inicialização de 99 dólares por assento, que é $495 em uma ordem de cinco lugares — a primeira fatura inteira — citado como se não fosse nada. O stepper do assento correu para 9.999 qualquer plano foi selecionado. **************************** Sempre implementou os verdadeiros tectos, para que alguém pudesse construir uma ordem de entrada de quarenta lugares, observar um total somar para ele, e ser recusado em salvar sem nada apontando para o número que Estava errado. Para agora no limite do plano e diz qual o plano que o define e o que mais carrega; Estar deficiente até que seja escolhido um plano, uma vez que o limite máximo é propriedade dessa escolha; e escolher um plano menor reduz a contagem com ele em vez de preço de cinco lugares para doze pessoas até os objectos do servidor. clampSeatsToPlan é compartilhado com o coletor e testado, porque a mesma regra tem de ser mantida quando os SEATs se movem e quando o PLAN o faz. Além disso: o CTA primário era o azul padrão da MUI — em um console de rótulo branco, a cor em a página pertencente a ninguém, no único botão mais importante.
· kamo-internal
Uma tela de números, então chamadas e textos podem vir de diferentes empresas
Uma organização compra seus números de uma empresa e responde às chamadas com outro. Isso é comum, e Kamo não poderia descrevê-lo: texto era uma propriedade de um servidor de telefone de um membro, por isso uma org cujos dids e SMS vieram de Telnyx e cujas chamadas foram respondidas por seu próprio FreePBX não poderia texto em tudo. Configurações → Telefone → Números é onde isso é dito agora, e fica entre Provedores e Extensões porque essa é a ordem em que o trabalho acontece. Uma linha lê como uma frase - "+1 (949) 555-1212 — Sarah Chen · Chamadas anel em Office PBX · Textos passam por Telnyx" — não uma grade de IDs de provedor. O número e a pessoa vem primeiro, porque essas são as duas coisas um administrador Na verdade, sabe. A decisão que vale a pena defender, e o um NumbersTab.test. tsx existe para proteger: um servidor de telefone que não consegue texto é OFERECIDO no dropdown de Textos, desativado, com A razão ao lado. Filtrar é a implementação óbvia e é aquele que deixa alguém preso — um administrador que não consegue encontrar o telefone sistema nessa lista conclui que a tela está quebrada, enquanto um que lê "FreePBX não pode enviar mensagens de texto" sabe adicionar um provedor de texto em vez disso. As razões vêm do servidor, onde cada provedor escreve o seu próprio. "Encontrar meus números" importa o que os fornecedores já sabem, porque digitando trinta DIDs corretamente não é uma coisa a pedir a ninguém e um erro de digitação falha silenciosamente. Telnyx junta-se aos dois. Seu cartão gasta a maior parte de suas palavras no público webhook chave: está em uma página diferente do portal Telnyx da chave API, e faltando Não tem nenhum sintoma sobre o envio — textos saem perfeitamente e cada resposta é recusada. O cartão também diz na frente o que Telnyx não tem, então isso é uma decisão alguém faz antes de cometer em vez de uma descoberta depois. Navios Kamo nenhum logotipo Telnyx; uma inicial colorida é honesta sobre ser nosso, onde algo desenhado Parece que a sua marca não seria. Duas coisas encontradas durante a construção: - NumbersTab reload dependeu de `t`, e useTraduções não promete um identidade estável — então o efeito re- disparou em cada renderização e na tela nunca terminou o carregamento. Não é um erro, é apenas um spinner permanente. O teste apanhou-o. - O menu de configurações e o mapa da etiqueta do provedor ambos têm testes de cobertura que manutenção exigida para uma nova aba e um novo provedor. Ambos estavam certos.
· VOIPService
Roteie textos por NÚMERO, e adicione Telnyx em ambos os lados dele
Uma organização pode comprar seus DIDs e sua mensagem de uma empresa e responder Chamadas com outra. Kamo não poderia expressar isso: uma mensagem de um membro era um propriedade de seu servidor de telefone, assim uma org cujos números vieram de Telnyx e cujas chamadas foram atendidas pelo seu próprio FreePBX não poderia texto em tudo — FreePBX declara sms(false), o gateway recusado antes de qualquer provedor ser alcançado, e o conselho "adicionar um provedor de SMS" levou a um trilho que não poderia abrir um Conversa. Assim, a pergunta passou do provedor para o número. ORG PHONE NUMBER diz quem possui um número, o que responde às suas chamadas e o que carrega Os seus textos. TEXT ROUTE KIND fica ao lado do id da instância em vez de ser inferido a partir dele, porque as duas tabelas candidatas são tanto UUID-keyed e entregando um id à fábrica errada não falha alto — falha como "nenhum tal instância", e O texto nunca é enviado. InboundRail já documentou esse perigo. TextRouter resolve em três níveis: a própria rota do número, em seguida, um padrão chamado pelo servidor de telefone (assim 200 DIDs em um PBX é uma configuração, não 200 linhas), então o próprio servidor de telefone — que é exatamente o comportamento de hoje, então uma org nunca abre a tela Números continua trabalhando não tendo configurado nada. O trilho finalmente herda o contrato de envio. TextSender tem dois adaptadores e SmsGateway fala com ele, então BulkTextProvider.send Texto — cujo erro é a mensagem sempre alguma operadora e.getMessage() — não pode mais colocar uma SocketTimeoutException na frente de alguém a tentar enviar uma mensagem a um cliente. E BulkTextInboundController agora entrega suas mensagens para VoipMessageService. ingerir em vez de gravar uma palavra-chave E parar, então uma resposta em um número de portador se torna uma conversa. Palavra- chave manipulação não se moveu para fora; moveu-se para baixo, para a sequência de cada outro trilho Já partilhas. VoipNumberOwnership ganha SOURCE ORG NUMBER na maior precedência: os outros dois fontes infer propriedade de uma coluna que por acaso detém um número, enquanto esta lê-se uma linha cujo propósito é dizer quem é o dono. Telnyx chega como um servidor de telefone e uma operadora. Como servidor de telefone é o primeiro um honesto sobre não ser um PBX: sem mensagens de voz, sem dispositivos, sem filas, não agente estados — e cada uma dessas bandeiras falsas agora carrega uma sentença de um membro pode ler, porque uma capacidade que é falsa sem razão escrita ecrã vazio com aspecto de trabalho. Isso é o que CapacidadeLimite e Capacidade de Telefone são para; SMS teve este tratamento desde smsUnabilableReason(), e o resto de A superfície agora também o tem. Sua credencial softphone é cunhada com o próprio ORG API chave, então ao contrário RingCentral ninguém tem que a bordo de cada membro em uma transportadora Portal. Duas coisas que vale a pena saber: - OrgPhoneNumberService leva PhoneProviderFactory @Lazy. O ciclo é real e legítimo, e Spring Boot proibiu referências circulares por padrão desde 2.6 — sem ele o contexto não começa, e nenhum teste unitário aqui seria capturado Isso, porque todos eles telegrafam os doutores à mão. - Uma org somente texto (números e uma operadora, nenhum servidor de telefone) pode enviar, mas não pode thread a reply: VOIP CONVERSATIONS.INSTÂNCE ID NÃO é NULL e parte do índice de dedupe. Essa queda é agora um ERRO nomeando o número em vez de um aviso. A DDL de ORG PHONE NUMBER é aplicada manualmente — docs/sql/. O serviço inicializador não é implantado neste cluster.
· kamo-shared-library
Telnyx, o primeiro provedor que é tanto um servidor de telefone e uma operadora
Telnyx vende o tronco SIP, DIDs e mensagens, e uma organização pode comprá-los metades independentemente. O caso é para: DIDs e textos comprados de Telnyx, com as chamadas respondidas pelo próprio cliente FreePBX. Colapso-o num só entrada forçaria aqueles a ser a mesma decisão, por isso TELNYX é anexado a ambos VoipProviderTipo e BulkTextProviderTipo. Ambas as enums são EnumType.ORDINAL, então ambos os anexos estão no final. O texto em volume tinha essa propriedade documentada em seu javadoc e aplicada por nada; agora tem o mesmo teste ordinal que o telefone enum tem, afirmou onde alguém adicionando um O provedor já está sendo feito para parar e pensar. Telnyx contas TERCEIRO PARTE: a org detém sua própria conta Telnyx e paga Telnyx por minuto e por mensagem, então o que Kamo conta é a integração. An linha Telnyx não atribuída não custa nada, o que é certo — uma credencial de telefonia sem nenhum membro por trás é um com o qual não estamos fazendo nada. Sem bump de versão. 17 serviços pin 1.6.0 e cada serviço Dockerfile reconstrui esta biblioteca a partir de main, para que um galo iria deixar todos eles resolvendo uma versão que não existe em lado nenhum. Adições recentes aos tipos/ enviados da mesma forma.
· kamo-internal
A prateleira substitui os cartões por aplicativo, e quatro correções para suas linhas
A plataforma de lançamento era uma parede de um cartão por aplicativo. A App Shelf lista cada uma das esses aplicativos em um único painel, com o que cada um faz e tudo dentro dele, então A grade de azulejos estava dizendo os mesmos nomes duas vezes. As cartas desapareceram; o que é esquerda na plataforma de lançamento é a prateleira, os widgets e as notas presas do membro. Re-ligar um aplicativo não-pined movido com eles — que era o controle de canto do cartão, E o alfinete da prateleira é agora. HomeCardPinControl e appPanes foram com o cartões; LEAF TOOL KEYS foi a única coisa no appPanes que sobreviveu a eles e agora fica no navRegistry, ao lado das opções que descreve. Quatro fixações na própria prateleira: - O chevron vem antes do pino, para que cada pino se senta na mesma linha vertical se a sua linha abre ou não para qualquer coisa. Verificado em 8 linhas, um x. - A metade aberta agora cresce: é um ITEM flex de uma linha, por isso o seu eixo principal é a largura e o estiramento só atingiram a sua altura — razão pela qual as suas fileiras pararam por pouco da coluna que lhes fora dada. - Um único botão Open App é centrado em ambos os eixos, e dimensionado ao seu rótulo em vez que esticado através do painel. A lista de chumbo 8px é deixada com ele, ou é 8px de offset unilateral que impede o centro do centro. - Esse botão agora lê "Abrir aplicativo".
· BillingService
A taxa de inicialização de um plano, e a oferta ao vivo, no fio
PlanDto transportava apenas a taxa recorrente, de modo que cada superfície citando um plano citou um preço cliente não seria cobrado: a taxa de inicialização do KamoCRM é de $99 um assento, que é de $495 em um ordem de início de cinco lugares antes de um único mês é cobrado. A página de preços públicos tem sempre mostrado; as superfícies no aplicativo não tinha como. A carga útil do plano também agora carrega a oferta em andamento no mercado, resolvido pela mesma predicar que realmente desconfie uma ordem. Uma página citando preço de lista ao lado de um live 33%-off promoção é errado no desfavor do cliente e um citando o desconto depois que caduca está errado na nossa — nem um número uma página pode decidir por si mesmo.
· kamo-internal
O relatório está na gaveta, estantes e Persists
`SystemBugThread` renderiza tudo o que alguém disse sobre um relatório e cada status pelo qual passou, sob a chamada de status atual: as respostas de chamada "por que está aqui agora", que é o que a maioria dos leitores veio buscar, e a transcrição respostas "como chegou aqui", que é o que a quarta tentativa de uma correção precisa. Lê-se ************* e não os pontos finais da sessão de imprensa todas as outras transcrições no console usam. Os membros autorizados pela sessão; um tópico de bug é legível por todos para o qual o RELATÓRIO é visível, o que é muito um conjunto mais amplo do que as pessoas que o colocaram. Um diálogo agora coleta o comentário para todos os três status que carregam um. Eles divergir no texto e em se o campo pode ser deixado vazio, e ambos são lidos fora do status — assim estante e edição Persists recusar um em branco e uma negação ainda Não. O título segue o exemplo: uma negação explica uma decisão, uma estalagem explica um atraso, e uma correção falha relata o que ainda está quebrado, e direcionando todos Três "Recurso para negação" fizeram o último ler como um pedido de desculpas. Problema Persists é o seu próprio botão ao lado Fix Verificado e seu próprio ponto final, desenhado do `canReportIssuePersists' — a resposta do servidor, nunca derivada. O seu público é mais ampla do que Fix Verificado e seus status de partida são diferentes, então um cliente inferindo-o de 'canManage' ou de propriedade iria escondê-lo de exatamente o para quem existe. Está desactivado na lista de triagem pela mesma razão: A reabertura de um relatório iria ignorar a razão pela qual o status existe para carregar. `SystemBugDrawerTriage` ganhou uma correção que vale a pena nomear - o compositor da thread é uma área de texto na mesma página e vem primeiro em ordem de documento, e `[role="dialog"]` não distingue os dois porque a gaveta carrega esse papel também. Um não- vigiado a pesquisa digitou a razão da negação na caixa de comentários e enviou a negação em branco.
· EmailService
Variáveis de visualização para SYSTEM BUG COMMENT
Sem eles, o editor de modelos renderiza a nova letra com furos onde o número do relatório, o remetente e o botão ir, que lê como um modelo quebrado em vez de como uma prévia em falta. Nenhum corpo comentário entre eles, deliberadamente: a carta não cita um. A o tópico do relatório é legível por todos, mas esta carta é endereçado a uma pessoa e encaminha para frente como qualquer outro correio.
· InitializerService
System bug.media session guid, e seu índice de pesquisa
A coluna por trás do tópico de comentários de um relatório, correspondente ****************************(36), porque MEDIA SESSIONS. em si mesmo uma coluna de cadeia de 36 caracteres e um tipo de descompasso na junção é encontrado em tempo de consulta por quem é azarado. Nulable, e sem enchimento. Semear uma sessão por relatório existente significa escrever quatro tabelas na ordem certa com o guids direito, que pertence ao serviço que já sabe como — `SystemBugService.assureThread` faz no primeiro toque. Um nulo aqui significa "ainda não aberto", nunca um erro.
· SecurityService
Enviar para IA diz a todos na linha, não apenas o repórter
O envio envia a mesma letra que o MediaService faz, então seu público tem que se mover da mesma forma: cada participante na linha do relatório menos quem agiu, cada com sua própria saudação e seu próprio fuso horário, em vez do repórter sozinho. A caminhada participante é duplicada aqui em vez de compartilhada, e a duplicação é deliberado — este serviço não tem nenhum cliente MediaService, e adicionar um para um despacho pode endereçar um e-mail colocaria uma chamada HTTP no caminho da escrita de status. A regra que repete é duas linhas, e ambos os lados passam pelo mesmo 'SystemBugStatusEmail' predica.
· MediaService
O tópico por trás de um relatório, e os dois estados que precisam de um
`SystemBugThreadService` é todo o recurso: um relatório de MediaSession, o status eventos escritos nele com a razão que seu autor deu, os comentários de qualquer pessoa que pode ver o relatório pode sair, respostas, reações e por membro mudo. Cada ponto de entrada é SystemBugController, nunca MediaController. Isso não é tidinass — os endpoints de mídia genéricos autorizam por adesão à sessão, e um bug thread é legível por todos, o RELATÓRIO é visível, que é um conjunto muito mais amplo do que as pessoas que o colocaram. A adesão aqui significa "participado" e existe para decidir a quem está escrito. Roteando o fio através do próprio bug regra de visibilidade é o que mantém essa regra no único lugar que a possui. Os fios são criados vagamente e semeados, não recheados. Os relatórios já existiam antes threads fizeram, então `media session guid` é nulável e `assegurarThread` preenche-o sobre primeiro toque — escrever um evento CRIADO carimbado com o tempo de criação da própria linha, e movendo qualquer `negal razão` a linha ainda carrega para a transcrição antes A limpar a coluna. Sem essa semeadura, substituindo a coluna por um fio teria silenciosamente jogado fora a uma frase por relatório o velho projeto fez conseguir manter. `thread()` é a leitura escrita que o faz; `detail()` permanece somente leitura porque é o caminho quente. «ReportIssuePersists» é a acção mais vasta de um relatório e a única capacidade que não estreita pelo visualizador: qualquer um que possa vê-lo pode dizer que uma correção falhou, porque a pessoa que anda em uma correção quebrada muitas vezes não é a pessoa que Apresentei o relatório. Em vez disso, reduz-se por STATUS — um relatório que nunca afirmou ser fixo não tem nada a contradizer. O e-mail de status agora planeja uma letra por participante. `plans()` toma a público como parâmetro para que a regra permaneça testável sem um contexto de Primavera, e comentários gratuitos passeio ChatEmailNoticeService livro existente sob seu chave de modelo própria — uma letra por destinatário até ser lida, digerida e muda respeitada.
· kamo-shared-library
Arquivamento, correções falhadas e todo o histórico de comentários de um relatório
Um relatório manteve exactamente uma frase. `system bug.denial ration` foi substituído por cada movimento de status subsequente, então um bug que foi negado, explicado, reaberto, corrigido e encontrado ainda quebrado terminou com uma razão de quatro e nenhuma maneira de ler o Descansa. A correção óbvia é uma tabela `system bug history` com uma coluna de notas. Essa mesa então tem que crescer threading, editar histórico, reações, anexos, por membro mudo e estado não lido — todos os quais as tabelas de mídia-stream por trás de cada bate-papo e Todos os tickets de suporte já têm. Assim, um relatório recebe uma MediaSession em vez, sob um novo tipo `SYSTEM BUG`, exatamente como `SupportTicket` já faz: alterações de status são escritas como mensagens do sistema que carregam o motivo do seu autor, comentários como comuns. Dois status vêm com ele. SHELVED está estacionado-não-recusado, que `WONT FIX` poderia não diga — um é uma posição de fila e o outro é uma decisão, e um repórter disse "decidimos não" sobre o trabalho que está apenas esperando parar de arquivar relatórios. ISSUE PERSISTS é seu contrapeso: uma correção enviada que não se manteve. Ambos requerem uma razão; uma negação deliberadamente ainda não, porque forçando um só lá ensina as pessoas a escrever uma paragem completa. A adesão em um tópico de bug não significa o que significa em um bate-papo. O fio é legível por todo mundo o RELATÓRIO é visível para; `MediaSessionMember' mean "tem tomado parte", e decidir quem é enviado por e-mail e quem tem silenciado. É por isso que nada aqui é servido através de MediaController, cuja regra de acesso é a adesão. O público da carta de status segue: cada participante menos o ator, em vez do que o repórter sozinho. O velho "o repórter moveu-o, por isso não mande nada" veto Teria feito os Persistas não notificar ninguém — pelo menos de todos os que escreveram A solução que falhou.
· kamo-internal
Um esboço do documento, e um esqueleto na própria forma da página
Só que o herói era uma verdadeira direcção. Tudo o resto — as nove categorias do livro de contabilidade, o painéis de conta, o pacto, cada opção de atualização — era um div estilo, então um leitor de tela usuário tinha um marco na página mais longa na área de configurações e nenhuma maneira de se mover entre categorias. Eles são h2 e h3 agora, e o livro segue o padrão de acordeão ARIA com o título WRAPPING sua alternância: aninhado do outro modo que a categoria é anunciada como parte de O controlo e deixa de estar num sítio para onde possas saltar. O estado de carga era um spinner centrado, que é o instrumento errado aqui. Um pedido preenche a tela inteira, então o spinner sentou-se no meio do nada e o layout finalizado em seguida, chegou imediatamente e mudou tudo sob o leitor. O esqueleto segura o real proporções — a banda herói com o seu trilho, a coluna longa do livro, a coluna mais curta de painéis ao lado — assim, a chegada é um preenchimento em vez de um salto. Não tem movimento próprio. além da onda de MUI; a página tem exatamente uma animação deliberada e não deve ter que competir com o que estava na tela um momento antes. Testes afixam o contorno e o ninho de acordeão, além de duas coisas que o livro fica certo e parar silenciosamente de acertar: o catálogo "100.0000" renderizado como 100, e um recurso booleano nunca descrito como ilimitado.
· kamo-internal
Desenhar as regras de deus que o servidor detém agora
A guia Usuários fechou todas as linhas de Deus para todos, e ambas as metades estavam erradas. Muito solto, porque o bloqueio viveu apenas neste componente — os objectivos atrás Ele respondeu a uma chamada direta de qualquer pessoa segurando GERENTE USERS. Muito apertado, porque... também bloqueou o único chamador com direito à edição. GodAccountGuard é o autoridade agora, ea gaveta lê chamadorÉ Deus da mesma resposta que o linha: trancado para todos os outros, viver para um par, e disse em voz alta no tom de aviso quando está ao vivo, porque uma linha de Deus editável parece comum. A bandeira sintética foi o único controle que permaneceu vivo em uma linha de deus, e é o mais pesado no ecrã — impede a autenticação da conta. Ele vai para o mesmo portão que o resto. A identidade é retirada num alvo divino. O servidor recusa-o em ambas as portas, e um botão que é oferecido e depois se recusa lê como uma falha em vez de como É a regra. contaIsGod monta-se a partir do perfil da forma como membroType Já o faz, e o servidor diz apenas a um leitor elegível para Deus.
· SecurityService
Só um deus pode administrar um deus, e nenhum deus é personable
Os usuários alcançaram a identidade de vidro de ruptura. UserAdminController guardado apenas o usuário do sistema, de modo que um titular não-deus poderia desativar uma conta deus e revogar as suas sessões ao vivo, reescrever os seus identificadores de entrada, ou enviar uma senha reset para ele — cada um deles uma maneira de desarmar as pessoas que o desfazia. O console já desenhou a linha como intocável e disse isso em um comentário; nada atrás da tela concordou. GodAccountGuard aplica a regra de pares PlataformaAdministradorController já segurado sobre uma plataforma de deus DIREITOS para a conta esses direitos penduram. A conta era a mais fraca das duas portas: tomar a senha de alguém é um Superset de tomar seus direitos. Guarda todos os cinco caminhos escritos, e O braço PLATFORM ADMIN do UserAccountEditPolicy, que é o outro direito superfície — atualização de identidade do perfil do membro e endereço pessoal endpoints. As leituras permanecem abertas, para que o console possa explicar a recusa. O alvo falha fechado e o chamador falha em aberto, deliberadamente: uma linha que não pode ser lido é deus, uma linha que não existe, e um chamador que não pode ser resolvido não é. Só sempre levantar uma restrição é que decide Para onde cada lado vai. A imitação tem o mesmo alvo recusado, sem qualquer paridade. A A porta do Cross-org sempre recusou um deus. a porta do mesmo-org atrás da página de configurações nunca fez, o que o tornou mais barato das duas entradas. Se passar por um deus dá a última chamada da plataforma de volta, por isso não admite exceção — nem mesmo para outro deus. Respostas agora carregam chamadaIsGod, e a carga útil membro carrega contaIsGod para um leitor que é elegível para Deus. Sem eles, a consola tem de trancar todas as linhas de Deus. para todos, incluindo a pessoa com direito à edição.
· kamo-internal
Pessoal pode escrever a sentença que o cliente lê
A nota visível do cliente tinha uma coluna, um campo DTO e uma página de plano pronta para processá-la, e Não há maneira de alguém o definir — então Harmony era o único que existiria, e apenas porque foi semeada à mão. É uma segunda caixa em vez de um botão "mostrar isso ao cliente" na nota interna. Um switch convida a reutilizar uma frase escrita para colegas, e a nota interna é onde pessoal registrar o que foi concedido e para quem; ele só pode permanecer sincero se publicar algo é um ato de escrita separado. Vazio permanece a resposta normal — um acordo com nenhum cliente a nota ainda é traduzida na íntegra a partir de seus termos estruturados. Ambas as notas estão agora rotuladas na lista, porque a diferença entre elas é a totalidade O ponto e um segundo parágrafo não marcados são mais do primeiro.
· kamo-internal
O softphone recebe uma caixa de entrada, e uma janela SMS é um tópico
A janela de ferramentas SMS abriu-se em um dialpad de doze teclas e depois tornou-se um três-tab window -- Chat / Templates / CRM -- intitulado "Conversa #a3f1c8de-...". Nenhum de é assim que alguém usa mensagens de texto, e duas das três abas não eram reais: O SmsCrmTab foi o DUMMY CONTACT e o DUMMY ACTIVITY ("John Smith, Acme Corp"), hardcoded, mostrado a cada membro sobre cada cliente) enquanto um genuíno inverso pesquisa já existia em SoftphoneCRM, e SmsTemplatesTab ler um cinco-item Constante enquanto o Controlador SmsTemplate do VOIPService não era chamado por nada. Também não havia nenhum lugar para ver seus textos: a janela mostrou uma conversa e não foi possível alcançar outro, então as únicas rotas para um thread foram uma entrada notificação estourando uma janela em você, ou no Centro de Interação. A janela - Dois estados, sem abas: um receptor, ou um tópico. Modelos movidos para o botão de parafuso do compositor na API real; detalhe de contato para as informações do cabeçalho renderização de painéis SoftphoneCRM. SmsCrmTab, SmsTemplatesTab, smsTemplateData e SmsNewConversation são excluídos. - Compose é receptor-primeiro e não tem caixa de mensagem. É isso que faz o caso fechado e focado seguro: uma janela que fecha a favor de uma já aberta fecha vazio pela construção, assim "uma janela de ferramenta nunca deve perder o que um membro datilografado" mantém sem qualquer manipulação cuidadosa. - O painel de contato troca com 'display', nunca uma renderização condicional -- que destruir o compositor cada vez que alguém olhou para ele, que é o falha a barra de tabulação velha continuou a produzir. Uma conversa, uma janela A loja de ferramentas sempre dedurou em adereços. conversaçãoId -- des- minimizar, Levanta, brilha. Não foi possível ver uma janela SMS aberta pelo NÚMERO DE TELEFONE, porque O SmsTool resolveu o id no estado local e nunca disse à loja, por isso abriu Essa conversa de qualquer outro lugar construiu uma segunda janela em cima da primeira. SmsTool agora patches o ID em seus próprios adereços (como ChatTool faz para o seu sessionGuid) e titula a janela com o contato. Escolher um destinatário estabelece três maneiras: criar aqui, adotar aqui, ou levantar a janela que tem E fecha esta. O softphone Uma quinta aba emblemática TEXTS, composta do que já existia para o Interaction Center -- useConversationRows' text bucket, ConversationRow, não lidoForRow, openConversation -- mais pesquisa e um botão de nova mensagem. A emblema lê UnreadContext.bySurface.sms, já vive sobre o soquete, então Não custa nada. Nomes de contacto ConversaRow admitido "não há nenhuma procura reversa do lado do servidor de um número para um nome de contato, então uma linha de texto é intitulada pelo seu número". Novo * ************************************************************************************************************. contatos por canônicoPhone, no navegador porque o destinatário procura cargas De qualquer maneira. Um índice retitula o cabeçalho, a lista TEXTS e o navegador. Também fixo: SmsNotificaçãoOuvir carimbado cada quadro de tomada INBOUND e ignorou a direção que o evento sempre levou, então o próprio membro saiu o texto voltou renderizado como do cliente e levantou uma janela para eles para Algo que tinham acabado de fazer. A janela agora fecha toda a superfície de texto sobre GET /sms/capability em vez de deixar uma equipe ou FreePBX org descobrir por pressionando enviar em silêncio, e um envio recusado mostra a sentença do próprio provedor com a mensagem marcada Não entregue. Requer o commit voipservice que adiciona /sms/capability e o resultado do envio.
· kamo-internal
Uma pílula de plano livre, e uma página em / meu plano atrás de ambos os comprimidos
A pílula experimental viveu duas vezes — o trilho de navegação e a plataforma de lançamento — com a contagem regressiva, o gate do tipo membro e o estilo copiado entre eles. Ambos presos em um julgamento vivo, então um organização cujo julgamento tinha acabado de terminar não viu absolutamente nada: nenhuma pílula, nenhuma explicação, não sinal de que o produto tinha se tornado menor. E nenhuma pílula foi a lugar nenhum, então um membro que a contagem regressiva não teve próximo passo. Agora ambos os estados são definidos uma vez, no PlanPill, que mantém as duas formas do trilho e o launchpad é o terceiro ao decidir o Meaning em um lugar. Mostra a todos os membros da equipa e LINKS apenas para alguém que pode agir sobre o plano; para todos os outros a dica é o todo o recurso, que é por isso que cada estado tem um nome gêmeo somente de leitura para quem perguntar. /myplan responde onde a organização está, quanto tempo ela tem, o que ela pode usar, o que um um plano maior acrescenta, e os termos que negociou. Três coisas que valem a pena guardar: O termo trilho é o herói, e é um dispositivo com três significados — um julgamento drenando para seu prazo, um plano pago que se move através de seu período de faturamento, um julgamento expirado no seu final. Ele é colocado para fora em sua largura verdadeira e animado com um quadro de chaves em vez de do estado, assim que um script lento ou uma hidratação falhada ainda mostra a figura real em vez de uma barra vazia. O livro de registos imprime valores, não tiques. O catálogo contém os números reais — 100 contactos, dois gasodutos, 4 GB por assento, ilimitado — e é sobre isso que um proprietário decide. Excluido linhas ficar, desmaiar e ordenado abaixo: eles são o argumento para atualizar, e escondê-los faria o plano livre parecer completo. Atualizações são uma diferença, não uma grade de preços. O leitor já tem um plano; sua pergunta é o que eles têm que eles não têm. Encomendado por preço — encomendado por quantas linhas um plano ilumina-se parece equivalente e não é, uma vez que um plano maior sobreposição pode adicionar menos. Comprar ainda acontece em BillingConsole, que este deep-links pela sub-tab. Reconstrução O checkout aqui seria uma segunda implementação da única coisa que não deve ter dois. A paleta segue a convenção da casa — uma rampa --plan-* misturada de --color-primário — com uma exceção: as cores do estado são fixas, porque uma organização cuja marca é âmbar usaria um crachá de teste em sua cor paga, e a única coisa que esta página existe dizer seria o que não podia dizer.
· BillingService
Um ponto final atrás da página do próprio plano do cliente
GET /api/billing/self/plan responde tudo /myplan renders: o plano, o que o organização pode realmente usar, o catálogo para comparar, assentos e sua lista, armazenamento, renovação e qualquer acordo negociado. Um pedido em vez de seis, por uma razão para além dos estados de carga: o nível de acesso não pode ser decidido honestamente no navegador. Decidir lá significa listar a Conta Os membros e um membro só podem listá-los se já tiverem o acesso decidido. Então... é decidido aqui, duas vezes — a porta do cliente decide o que OFERTA, isso decide o que pode ser ler — e um chamador SELF PAY recebe uma carga útil a lista de lugares, totais de organização e Nunca se chegou a acordo. Retenção bate escondendo: alguém lendo a aba de rede de uma página que lhes é permitido abrir deve aprender nada que não lhes fosse permitido ser dito. O julgamento é a armadilha que vale a pena nomear. Uma assinatura de teste está no plan code = GRÁTIS, porque TrialAdminService converte a linha free-default no lugar, enquanto IntitlementService concede todo o catálogo ilimitado. Servir as próprias linhas de recursos do plano durante um julgamento seria dizer a um cliente que eles não têm IA, e-assinaturas ou SSO enquanto eles estão no meio de usando todos os três — na semana eles estão decidindo se comprar. Assim, um teste ativo relata a união do catálogo, conforme incluído. Um julgamento PENDING deliberadamente não: seu relógio não começou, e prometendo de outra forma é uma mentira o produto contradiz no primeiro clique. O acordo é devolvido derivado, nunca transcrito. A prosa é clienteVisibleNote e apenas isso; notas internas nunca deixar o serviço. E o custo do próximo lugar é um número de SetupFees.forAdded Assentos — o mesmo código que aumenta a carga — em vez de um sentença descrevendo a regra, porque uma sentença é uma segunda implementação que eventualmente discordam do primeiro.
· SecurityService
Diga em que plano está uma organização, e quem pode mudá-lo
O console poderia dizer que um julgamento estava em andamento e nada mais. direitoFonte é "TRIAL" ou nulo, então livre, expirado-julgamento, pago e compaded organizações eram todos iguais — é por isso que uma organização cujo julgamento tinha acabado de terminar não viu nenhuma pílula, não explicação, e nenhum sinal de que o produto tinha se tornado menor. planMode nomeia o estado e o planoAccessTier nomeia quem pode agir sobre ele. Ambos andar o direito payload cada página já carrega, então as pílulas que lê-los não custam nenhum extra No que diz respeito ao transporte rodoviário de mercadorias, a Comissão considera que o transporte rodoviário de mercadorias constitui um meio de transporte de mercadorias. A fonte está deliberadamente intocada. Tem dois valores hoje e é lido por dois sites de comprimidos mais, pré-autenticação, ************* que pinta as telas de login e registro; alargando-a mudaria o que todos os três significam em Uma vez. PRECEDÊNCIA: PAGAMENTO > COMP > TRIAL ACTIVE > TRIAL PENDING > TRIAL EXPIRED > LIVRE assinaturas filtradas pela regra compartilhada isLive — a mesma BillingService's DireitoO ModeResolver usa, então os dois não podem discordar sobre uma conta PAST DUE dentro seu período de graça. É suporte de carga: uma linha FREE DEFAULT é semeada na inscrição e nunca é removido quando um plano é comprado, então "a primeira linha encontrada" relatórios GRÁTIS para a maioria dos pagamentos Clientes. O nível é resolvido aqui em vez de no navegador porque o navegador não pode resolvê-lo honestamente: decidi-lo a partir de linhas conta-membro significa listar membros, que já exige que o acesso seja decidido. Um delegado conta apenas com um financiamento de conta organização — pessoal da plataforma administra rotineiramente Contas destinadas a outros locais.

Seu universo está esperando.
Comece gratuitamente em minutos.Adicione produtos sempre que estiver pronto — sem migração, sem novos logins.
Gratuito para sempre — não é necessário cartão de crédito