Ir para o conteúdo
KAMOUniverse

Changelog

O que há de novo.

Cada melhoria para a plataforma, como ele envia.

  1. · kamo-shared-library

    Bill todo o termo na frente, e dizer isso na citação

    Um org que cobra adiantado viu a taxa mensal em um negócio de três anos. A que decide que já existia — ******************* mas atingiu apenas QuoteConversionService, no momento da conversão. A O próprio documento foi sempre pago como se o cliente pagasse por período. applyOptionHeadlineTotais agora ramifica na base: FULL CONTRACT takes contratoTotal (que já carrega a taxa de configuração, por isso não deve ser adicionado duas vezes), First PERIOD PLUS SETUP mantém a primeira factura. períodoTotal e mensalEquivalente são intocados em ambos — os crachás de poupança comparar contra mensalEquivalente, e movê-lo mudaria cada bitola na página. A base é STAMPED na citação quando é levantada em vez de resolvido ao vivo. Uma citação declara os termos, e um administrador que muda de org na próxima semana deve não reiterá-los em um documento que um cliente já detém. Lendo a configuração org no momento da conversão fez exatamente isso: re-preço aspas que já eram aceite. Conversão agora lê o próprio selo da citação, e revê-lo carrega para que uma revisão não possa re-basear silenciosamente uma negociação ao vivo. A coluna é nulável porque cada linha existente a precede; null resolve para FIRST PERIOD PLUS SETUP, que é o que essas citações foram enviadas, então nada já nas mãos de um cliente muda de valor.

  2. · DaemonService

    Tique a varredura de relatórios programados do Serviço de Segurança

    A cada cinco minutos. Um cronograma é definido para o minuto, então um tick mais fino não compra nada Um membro notaria e um mais grosseiro faz um relatório das 8:00 chegar às 8:15. DaemonService possui isso pela mesma razão que possui o pacote legal e varredores de lead-email: réplicas: 1 com estratégia Recrear significa que o pod antigo se foi antes do novo começar, então uma janela de implantação não pode disparar duas vezes. A @Scheduled in SecurityService, que rola com a onda, pode - e não há ShedLock Em qualquer lugar desta frota, então a contagem de cápsulas é a garantia. @DisallowConcurrentExecution porque uma varredura longa não deve ter uma segunda iniciado em cima dele: ambos encontrariam os mesmos horários devidos, uma vez que nextRunAt é apenas avançou uma vez que o primeiro os reivindicou, e o mesmo relatório iria sair Duas vezes. Tempo- limite explícito pela mesma razão que os trabalhos de irmãos os têm — um padrão RestTemplate espera para sempre, e um fio preso aqui significa que a varredura nunca corre novamente até que o pod reinicia, que é indistinguível de "nada era devido". Nada sobre um relatório cruza este processo: a varredura retorna conta, nunca rema.

  3. · SecurityService

    Execute os relatórios que são devidos, e envie-os

    Um endpoint interno, X-Internal-Auth fechado e constante-tempo comparado, que O serviço de daemon marca. O cron fica no daemon porque é réplicas: 1 com estratégia Recriar, para que uma janela de implantação não possa disparar duas vezes; a @Scheduled in a service E não há nenhum ShedLock nesta frota. Para uma tarefa cuja saída são as figuras de uma organização enviadas para uma lista de endereços, "normalmente uma vez" não é uma propriedade. Cada programa é executado como seu PRÓPRIO, com direitos recomputados em tempo de execução doações armazenadas — não como o daemon, que não tem nenhum, e não como qualquer outro último editado O horário. Revogar o acesso de alguém para os seus horários de produção de dados eles não devem mais ver na próxima execução, em vez de sempre que uma sessão expira. Sem isso, uma programação é uma maneira de ter o servidor ler dados em seu para que você não pudesse ler a si mesmo. nextRunAt é avançado ANTES da entrega é tentado. Um crash no meio da entrega então custa um envio perdido em vez de um loop ilimitado de duplicatas, e um semanal perdido relatório é um fracasso muito mais barato do que o mesmo relatório que chega quarenta vezes. O arquivo renderizado vai como um anexo em vez de um link, porque estes são rotineiramente enviado para um cliente ou um contador sem conta. Um mau endereço faz não custou aos outros destinatários o seu relatório, mas uma execução onde cada endereço falhou é Não foi entregue a ninguém. Um relatório vazio não é enviado a menos que o proprietário pediu: um e-mail semanal que sempre chega deixa de ser lido.

  4. · EmailService

    Deixe um envio transacional carregar um anexo

    Adicionado para relatórios agendados, que são um PDF renderizado ou pasta de trabalho que tem que chegar a pessoas que podem não ter conta — um link não seria uma entrega para eles. A camada SMTP tomou anexos desde que foi escrita; este caminho simplesmente passou null. Então nada sobre como o mail é construído muda, o campo é opcional, e cada A chamada existente toma exatamente o caminho que sempre fez. Base64 em vez de multiparte porque este endpoint é JSON servidor- a- servidor e Cada pessoa que liga já constrói um corpo JSON. Os anexos sobredimensionados e malformados são RECUSA em vez de truncada: uma pasta de trabalho de 40MB repelida custa o domínio de envio reputação, e um silenciosamente truncado é um arquivo que o destinatário não pode abrir e não posso explicar. O lançamento é o que permite ao chamador gravar uma execução falhada em vez de uma Entreguei um.

  5. · kamo-shared-library

    O e- mail de um relatório agendado chega

    RELATO SCHEDULED, registado em CANONIC KEYS — ausente dessa lista é um 404 permanente, quaisquer arquivos que existam ao seu lado. O organismo indica o âmbito dos dados. Um destinatário que não seja o proprietário do calendário não tem outra maneira de dizer um total de toda uma organização de um cobrindo apenas o que o proprietário pode ver, e esses são documentos diferentes. Também nomeia o proprietário duas vezes: uma vez como que estabeleceu o calendário, e uma vez como de quem são estes números.

  6. · SecurityService

    A lista de uma janela de partilha escolhe e um alvo de partilha que se encaixa em cada ID

    - Está bem. retorna o povo da organização, departamentos e papéis como opções de escolha, em uma chamada e em uma forma. Construído em os prestadores de opções de comunicação de informações em vez dos membros, departamentos e funções endpoints: aqueles que cada um responder a uma pergunta de tela diferente e retornar um diferente forma, e um diálogo de partilha consumindo todos os três iria quebrar sempre que qualquer um deles Alteração por razões não relacionadas com a comunicação de informações. Gated on SHARE REPORTS — a lista de todos na organização não é algo para entregar a um membro sem razão para ver. Um alvo de partilha é agora uma sequência. Um ID de membro é um departamento ou função id é um UUID; a coluna Longa com que esta começou pode conter metade os tipos, e a metade que não poderia ter falhado em silenciosa correspondência Ninguém. O departamento e as verificações de funções comparam-se como texto e resolução por membro AGORA, em vez do que aconteceu com uma sessão quando a ação foi feita. Cada uma das três verificações de adesão falha. Uma parte que não pode ser avaliado não concede.

  7. · kamo-internal

    Passo através de cada hit de busca, não só o mais novo

    A pesquisa no topo de uma janela de conversação marcou todas as ocorrências da pesquisa e depois Rolou para exatamente um deles — o mais novo — sem nada para dizer quantos outros Não havia como alcançá-los. Um membro que procura uma conversa por uma factura número podia ver a palavra realçado três telas para cima e não tinha nenhum controle que iria Leva-os até lá. Agora se comporta como a barra de encontrar já se assemelha: um contador lendo "3/12", uma seta para as mensagens mais antigas e uma para as mais novas, Enter e Shift+ Digite para o mesmo dois movimentos, e a batida sendo estacionado em desenhado em âmbar sólido contra o amarelo do Descansa. Pousar no mais novo hit é inalterado, então um membro que só digitou uma consulta e ler o resultado vê o que eles sempre fizeram, agora com a contagem ao lado dele. Duas decisões por baixo valem a pena soletrar. As batidas são contadas por ocorrência em vez de por mensagem, e contra as palavras o bolhas realmente desenhar — a tradução onde uma mensagem tem uma, que a pesquisa antiga Não o fez. O contador e as marcas são duas visões de uma lista produzida por um único scan (encontrarOcorrências, compartilhadas com o realçador), então "12" é sempre exatamente o número de marcas no ecrã. Dois exames que discordavam mesmo ligeiramente deixariam setas Aterrando em nada. paged: as mensagens chegam ao fundo enquanto uma pesquisa está aberta e as páginas mais antigas como "result 3" se tornaria silenciosamente uma mensagem diferente. Segue-se que "1 de 3" no mais antigo golpe não significa que não há nada mais velho — apenas que nada mais velho foi buscar — assim, sair de lá pede a página anterior e aterra no que traz, o mesmo movimento que a lista já faz quando alguém rola para o topo. Enrola para o mais novo apenas uma vez que o servidor se esgotou, ou depois de cinco páginas chegaram não carregando nenhum hits em tudo: um imprensa de uma seta não deve ser capaz de andar um ano-long de volta à sua primeira mensagem. A geometria de repouso da barra de ferramentas está intacta. O contador e as setas aparecem com o consulta em vez de com o campo, porque eles custam cerca de sessenta pixels e um vazio A caixa de busca não tem nada para contar.

  8. · kamo-shared-library

    Um relatório de serviço-trabalho e um alvo de partilha que se encaixa em cada ID

    ServiceWorkReport é o décimo relatório de sementes e aquele que faz escopo de nível de linha real em vez de meramente presente. O comércio transporta o próprio/todos os pares da plataforma — VER OWN SERVICE WORK senta-se ao lado de VER SERVICE WORK como um irmão — e uma visita um técnico que detenha apenas o direito mais estreito vê seu próprio trabalho enquanto seu supervisor vê a filial. Uma definição, dois respostas, e nada inventado para reportar. Sem ele, eu e a equipe seriam caminhos não testados na parte deste sistema é menos aceitável deixar não testado. As filas são visitas, não empregos: um trabalho com três visitas é três trabalhos, Talvez por três pessoas em três dias, e derrubá-los faz "quem fez o quê, quando não é possível responder. ReportViewShare.targetId torna-se target key, uma string. Um ID de membro é um de 19 dígitos Longo, mas um departamento e um papel são UUIDs, então uma coluna INT8 poderia segurar metade do tipos de ações — e a metade que não poderia ter falhado por correspondência silenciosa Ninguém. Renomeado em vez de retipado porque a atualização do esquema do Hibernate adiciona um coluna e não retype uma; a coluna antiga nunca foi escrita. LeadPipelineConversionReport agora aplica o piso não atribuído da própria grade. Sem ele, o relatório constitui uma segunda forma não auditada de ver o conjunto não atribuído, e suas contagens de grupo discordam de /leads sem razão visível.

  9. · kamo-internal

    Pin as pastas bem conhecidas para o topo da barra lateral

    Caixa de entrada, rascunhos, enviado, lixo, lixo e arquivo parar de se mover. Eles mantêm essa ordem. no topo da lista de pastas, e tudo o que o membro fez fica por baixo sob um cabeçalho "Minhas Pastas", ainda livremente reordenável. As linhas fixas não estão registadas com o dnd-kit em tudo em vez de recusar o drop: uma linha que apenas descodificadores para mover ainda é um alvo drop, então soltar um pasta para a caixa de entrada iria animar toda a lista e, em seguida, silenciosamente colocá-lo Para trás. Eles mantêm a coluna de aperto como espaço vazio, então ambos os grupos compartilham uma esquerda edge em vez de ler como duas listas. Pinning é deliberadamente mais estreito do que isSystemFolder, que corresponde à folha em qualquer profundidade para os próprios Clientes de um membro. O arquivo desenha o ícone do arquivo. Este é o chamada certa para um ícone e o errado para uma posição, então pinedRank olha roots only — uma pasta que alguém arquivou sob seu próprio pai permanece seu Assim como as subpastas de uma pasta presa. O Spam fica na ranhura do Lixo. A infra- estrutura normaliza-se para o lixo onde quer que escreva, então um servidor listando "Spam" está nomeando a mesma pasta e não deve cair entre O próprio membro. O rank vive na pastaTargets, que já possui a pasta compartilhada vocabulário, assim que o seletor de movimento ordena suas raízes da mesma forma e os dois as superfícies não podem voltar a separar-se. aplicar oFolderOrder move para a lib/email ao lado it: vitest's include globs não cobrem app/hooks, então a lógica de ordenação foi deixada lá foi recolhido por nada e passou por nunca correr. Membros que arrastaram sua caixa de entrada antes desta mudança ainda nomeá-la em seu salvo preferência. Essas entradas são ignoradas, e o próximo arrastar as escreve.

  10. · kamo-marketing

    Reconstruir o índice como um mapa do produto

    Era uma lista recursiva de todos os trezentos artigos, marcada por profundidade. Tudo estava na página e nada foi encontrado, e a página pesava 3,9 MB. A forma é emprestado do produto. A porta da frente de um cliente KamoCRM é o Página de lançamento inicial -- uma grade de áreas que você escolhe -- então a frente do centro de ajuda porta é o mesmo gesto, e alguém chegando já sabe como lê-lo. As peças são um mapa de escala em vez de uma grade de cartões. Cada lista o que é realmente dentro dele com contagens reais, então uma telha é tão alta quanto sua área é grande: Cliente Gerente de Relações tem oito seções e quarenta e nove artigos e olha para ele, em seguida para Calendário com cinco. Cartas de altura igual com um ícone cada seria mais arrumado e iria deturpar o produto -- e dezesseis glifos contorno diferenciar Dezasseis áreas muito piores do que dezesseis tabelas de conteúdos fazem. Áreas que não têm Seções listam seus artigos em vez disso, então nenhum azulejo diz apenas seu próprio nome. A busca é a ação primária e agora está acima de tudo, classificado (um hit em o início de um título bate um mais tarde, que bate um no corpo) e agrupa por área, porque "qual parte do produto é este em" é a maior parte da resposta. Os fósforos estão marcados no local. Uma tira de ponto de partida para um visitante pela primeira vez, deliberadamente sem número: o três lidos como uma sequência e não são um, uma vez que o terceiro é de um administrador trabalho que a maioria dos membros nunca vai fazer. Contagens e sobrancelhas são definidas no rosto mono já na marca, eo bege secundário -- que o site quase parou de usar -- carrega o estrutura. Isso impede a página de ser monocromático verde sem introduzir qualquer coisa que já não seja KamoCRM. Páginas de categoria e seção também deixam de ser becos sem saída: "Nesta seção" agora mostra os links que o servidor nunca enviou, dois- up com excertos e um Conte onde uma seção tem uma.

  11. · kamo-internal

    O Estúdio de Relatórios — uma biblioteca e um renderizador para cada relatório

    /relatórios é uma prateleira, não uma grade de cartões: duzentas caixas arredondadas idênticas é um Wall to Scan, e uma linha dá a sala de um relatório para ser lido. Aninhar as vistas salvas no relatório a que pertencem, que é o que ensina a distinção Subsistema baseia-se em — não existe controlo "Novo relatório" em parte alguma, porque um relatório é uma classe Java e o que um membro salva aqui é uma configuração de um. O estúdio é três bandas: o que você está pedindo, uma frase dizendo o que está em ecrã, e a resposta. A banda do meio é de carga. Falha na tela do relatório quando alguém pode ver figuras e não pode dizer o que são figuras de, e cada pergunta nessa categoria — quantas linhas, ao longo de que período, estreitaram como, e cobrindo quanto da organização — enquadra - se numa sentença. A nota do escopo de dados senta-se lá verbatim, porque um total produzido sob "seus próprios registros somente" documento diferente e nada mais na tela os distingue. Nada no aplicativo/componentes/relatórios pergunta qual relatório está renderizando. visívelStudioControls lê as capacidades declaradas de uma definição e desenha o controles correspondentes; ParameterControl despachos em um tipo de parâmetro; cada célula renderiza através do descritor ReportFormat de sua coluna. Um relatório só de KPI recebe um período e um botão Executar e nada mais, sem uma linha de código que o saiba existe — e no dia em que isto deixa de ser verdade, adicionar um relatório deixa de ser um Java Mudança. formatters.ts é o único lugar onde um valor se torna uma string, e seu Java twin lê o mesmo descritor, então uma figura na tela e a figura em um PDF baixado não posso discordar. Datas passam por app/lib/serverTime.ts ao invés de um segundo analisador: Java escreve LocalDateTime sem deslocamento e um navegador lê isso como hora local, que é horas fora. A paleta do gráfico é um hex fixo, não derivado do --color-primary. Todos os outros cor aqui é uma tonalidade da marca da organização, que é certo para cromo e errado para um gráfico — um branco pálido-label primário daria uma paleta série ninguém podia distinguir. A ordem foi pesquisada e validada e não o globo ocular: todos os oito passar a faixa de leveza, piso de chroma, separação de DCV, visão normal verificação de piso e contraste na superfície leve deste aplicativo, que é a única superfície Sim. --report-* segue o padrão --legal-* e --training-* exatamente, e seu selo é --accent-work, que a paleta Painel de Instrumentos já nomeia como o sotaque para "tarefas, calendário, relatórios". Os relatórios ocupam o seu lugar na navegação, no espelho AppType, nas bandeiras da Organização e A árvore de permissão. Os seus cinco direitos são ingratos sem o último.

  12. · SecurityService

    Executar, exportar e agendar os relatórios do Report Studio

    O serviço metade da estrutura: o catálogo, a corrida, os catadores, quatro exportadores, visualizações salvas e agendamentos. Autorização é explícita em todos os caminhos, porque este serviço é executado a corrente de filtro. Dois direitos estão sempre em jogo — VER REPORTS abre o módulo, e o direito de domínio próprio do relatório decide se esse relatório pode ser executado e como grande parte da organização que cobre. VEZ REPORTS sozinho não admite ninguém a quaisquer dados, que é o que torna o aplicativo seguro para ligar para um inquilino. ReportScopeResolver lê os direitos de irmãos próprios/all existentes da plataforma e nada inventado para reportar. Um relatório que nomeie nenhum direito mais estreito é organização ampla e diz isso em uma nota o estúdio e cada impressão de exportação verbatim — um total produzido sob "apenas os seus próprios registos" não é o mesmo documento como um produzido sobre toda a organização, e um leitor não pode dizer Eles separados, caso contrário. sanitise() é o limite que mantém uma visão salva de se tornar um usuário definido relatório: uma solicitação só pode escolher entre o que a definição já declarada. Ele tiras colunas desconhecidas, agrupando níveis e parâmetros, mas parte deliberadamente uma coluna desconhecida num FILTER para o motor de consulta recusar — deixando cair um oculto coluna altera a apresentação, deixando cair um filtro muda a resposta. Exportações leram o mesmo RelatórioResultar a tela foi desenhada de e formatar através o mesmo descritor ReportFormat, então uma figura em um PDF e a célula de onde veio não posso discordar. A pasta de trabalho escreve números como números com uma máscara de formato que como strings pré- formatadas, que é a diferença entre uma planilha e um CSV com uma extensão diferente. O PDF carrega a organização, o período, o escopo de dados, quem executou e números de página. Um relatório agendado é executado como seu próprio proprietário, com direitos recomputados em tempo de execução, então a revogação do acesso interrompe um escalonamento na sua próxima execução em vez de quando uma sessão expira. nextRunAntes da entrega: não há nenhum ShedLock nesta frota e réplicas: 1 é tudo o que impede um envio duplo. pdfbox e poi-ooxml são adicionados AQUI e não ao kamo-shared-library, que cada componentes-scans de serviço — eles de outra forma enviariam para ~60 imagens que nunca Fazer um relatório. kamo.reporting.enabled está definido no ConfigMap deste serviço e em nenhum outro lugar; essa propriedade é o que impede um bean relatório sendo construído em serviços que não podem satisfazê-lo. Também muda o nome de uma entrada antiga. ec2ae5e renomeado ************* para #syncLabels sem mudar o nome linha de base, que falhou na catraca de origem/principal em ambas as direcções Uma vez. O manipulador resolve um chamador — getOrganizaçãoDePedido, 401 quando null — então isto é um renome, não um alargamento.

  13. · kamo-shared-library

    A estrutura do Report Studio, e nove relatórios construídos sobre ele

    Um relatório agora é uma classe Java aqui e nada em outro lugar. Definição do Relatório descreve o que é um relatório, o que pode ser perguntado, quais colunas tem e como cada um deve ser lido; o frontend renderiza o que quer que declare, assim adicionando um relatório não precisa de endpoint, nenhuma página e nenhum TypeScript. As peças que carregam o peso: - ReportCapability é por isso que o frontend pode ser genérico. Um relatório que declara apenas FAVORITE e SCHEDULE não recebe nenhum construtor de filtro, nenhum gestor de colunas e não painel de agrupamento — ManagementOverviewRelatório existe em parte como o canário para isso. - ReportFormat é um descritor, nunca uma string renderizada, então uma moeda no tabela, a dica do gráfico, o CSV e o PDF não podem discordar. - ReportSecurity O escopo vem dos direitos de domínio de cada relatório, nunca de um Relatório à direita. Um relatório de vendas amplia em View ALL LEADS exatamente como o líder O grid sim, por isso o relatório nunca pode ser uma forma de contornar o modelo de segurança. - ReportQuerySupport coloca o predicado de escopo em primeiro lugar, rejeita uma coluna desconhecida chave em vez de derrubá-lo, e esquerda-junta cada associação — uma associação implícita é um INNER um, e as linhas sem nada do outro lado são os Alguém está à procura. - ReportAggregator mantém um grupo nulo como seu próprio grupo, então subtotais ainda adicionam até ao total. - As datas se resolvem no fuso horário do MEMBRO e estão meio abertas. Às 03:30 UTC em 1 Setembro um membro em Los Angeles ainda está em agosto. Cada classe de relatório carrega @Component AND @ConditionalOnProperty. Isso não é decoração: cada serviço verifica com.kamo, mas declara seu próprio explícito @EnableJpaRepositórios lista, então um @Component nu precisa de um EntityManager é construído em todos eles e CrashLoops aqueles que não podem satisfazê-lo. A A propriedade está definida apenas no SecurityService, e ReportBeansAreConditional Falha no teste O edifício se um relatório o esquecer. Relatórios é um aplicativo (ServiceType id 32) com cinco direitos (295-299). VER REPORTES é a porta do módulo e não concede dados por si só. Os nomes VER REPORTES e EXPORT REPORTS existiam nos IDs 1 e 2 até 2026-08-14, quando foram removidos junto com as páginas do stub por trás delas; os direitos persistem como getId(), então nenhuma concessão feito antes então pode resolver a estes. PhiModule.REPORTING é PERMITIDO - o motor lê em-cluster e armazena no Entendido. Exportar e agendar deixam o limite e são regidos por seus direitos próprios e por EMAIL.

  14. · kamo-internal

    O importador, e três coisas que nos ensinou

    Publica o corpus dirigindo a verdadeira API KBService em vez de escrever linhas -- um INSERT direto salta a linha de versão, o kb.article. evento publicado que é a única coisa indexando um artigo para o assistente de IA, e o Fan-out da tradução. Uma importação que parecia bem sucedida e deixou "Train AI With Este artigo "não fazer nada sobre trezentos artigos é o tipo de falha Ninguém repara durante um mês. Três defeitos surgiram, todos aqui: IDs de 19 dígitos nunca devem tocar em um número JavaScript. Número (11684856209608710') é 1168485648209608700. Uma sessão cunhada com esse id org autentica perfeitamente e resolve para uma organização que não existe -- a visão geral voltou vazio e lido como "a base do conhecimento é vazio". A mesma armadilha a segunda vez no artigo uid, onde reorder respondeu 403 porque o pai A identificação tinha sido arredondada e não correspondeu a nada. Os IDs da sessão estão agora escritos no JSON como literais brutos, e IDs de artigo são lidos fora do texto de resposta antes JSON.parse pode tocá-los. O pai nulo de uma categoria é a seção, não a raiz. Enviando null teria publicou dezesseis artigos de nível superior irmãos, e o site de marketing -- que perguntas para a árvore abaixo help-center -- teria mostrado uma página vazia. Kubectl port-forward não sobrevive a alguns milhares de pedidos. Transportes falhas agora tentar novamente com backoff; erros HTTP não, porque repetindo um 403 quatro vezes só leva mais tempo para falhar. O autor é Sage, que já é autor do artigo da Central de Ajuda. Um artigo autor e último editor vêm da sessão que o escreveu e são mostrados por nome em cada artigo na consola.

  15. · kamo-shared-library

    Persistir regra de cotação-contato de um mercado, e recusar um meio-declarado

    Os DTOs realizaram cotaçãoContatoMode e cotaçãoContatoMembro mas nenhum escritor leu eles, de modo que a configuração round-triped através do formulário e foi silenciosamente deixado cair. Ambos os caminhos criar e atualizar agora aplicá-lo. O membro é mantido apenas sob EXPLICIT e compensado de outra forma, de modo que um mercado que o regresso não deixa uma pessoa para trás para ser reintegrada por um retrocesso posterior, e EXPLICIT nomear ninguém é rejeitado em vez de discretamente rebaixado — o versão silenciosa imprime um nome diferente na próxima citação do que quem salvou o formulário esperado. A pesquisa é org-scoped: IDs de membros são globalmente únicos, então um um não vigiado deixaria um inquilino nomear o pessoal de outro inquilino em seu próprio documento.

  16. · kamo-internal

    Escolha quem cita os nomes como contato do cliente

    A janela de mercado ganha o "Contato Futuro do Quote" em Activar aspas: agente atribuído, uma escolha feita por citação, ou uma pessoa explícita escolhida com o mesmo AgentSelector o filtro /leaders usa. Em Ajustável Por Citação o editor cresce uma seção de Contato, semeada a partir do instantâneo que o servidor tomou quando a citação foi levantada — que é o agente do líder, por isso abre-se para a pessoa já presente — e salva a mudança com o resto da citação. As três decisões do lado do cliente saíram de dois grandes componentes para app/lib/quotes/quoteContact.ts. Eles foram questionados em ambos os ecrãs e os ecrãs discordar é invisível até que um documento imprima a pessoa errada; como um módulo eles também são testáveis, o que inline ternários em um diálogo de 1300 linhas não foram. A pré-visualização mostra um novo contato escolhido pelo nome com o título, telefone e e-mail em branco: estes estão resolvidos lado servidor da caixa de correio e extensão desse membro, assim carregando o número da pessoa anterior sob um novo nome seria uma mentira o O banner de mudanças não salvas não desculpa.

  17. · SecurityService

    Honrar a regra de contacto de um mercado onde quer que uma citação nomeie alguém

    Os detalhes da equipe, a visualização da equipe e a própria página do cliente agora resolver o contato através de um método, para que eles não possam discordar sobre quem é impresso no mesmo documento. Em ASSIGNED AGENT que é o agente principal resolvido por leitura, como antes; sob os outros dois modos é o instantâneo que a citação carrega, com o agente do lead como recurso para uma citação levantada antes da configuração existir ou alguém que nomeou alguém que já partiu. Criar selos do instantâneo do mercado em vez do corpo de solicitação, e atualização aplica um novo contato apenas quando o mercado roda ADJUSTABLE PER QUOTE e o membro pertence à org — um mercado que nomeia um contacto explícito E um PATCH feito à mão é como essa promessa seria quebrada. QuoteController cinco manipuladores construídos cliente/agente/ajustável separadamente; eles agora compartilham um staffView, que é o que mantém os três consistentes.

  18. · kamo-internal

    O corpus do artigo e o oleoduto que o constrói

    307 artigos de 16 categorias e 29 secções, do projecto 248 artigos sob Documentação/ mais 59 escritos aqui: cada categoria e landing page da seção, um conjunto de Introdução cobrindo a integração da rascunhos nunca alcançados, uma categoria de Plataforma a pasta de código- fonte só teve README para, e os dois artigos de criação de membros que estavam simplesmente faltando. Quatro peças: lib/markdown-to-lexical Marque para a forma que as lojas de base de conhecimento. O conjunto de nós é uma lista branca do que ambos os renderizadores já lidam, porque o renderizador público desenha um nó desconhecido como um div nu -- nenhum erro, nenhum aviso, um parágrafo que perdeu silenciosamente seu estilo em uma página ninguém é A observar. As listas foram a parte difícil: uma linha em branco não deve terminar uma (o rascunhos têm lacunas de captura de tela entre as etapas, e terminando lá reinicia o a numeração em 1) e um item indentado é uma sub-lista, não uma frase anexada para o passo acima. lib/clean-source O que um rascunho deve parar de dizer antes de ser publicado. Constantes de permissão tornam-se as palavras na tela; nomes de parênteses um ficheiro .tsx ou um campo de entidade ir inteiramente; a caixa de verificação de revisão e o rascunho-método de chamada ir; e referências cruzadas são renomeadas em vez de excluído, então "ver Como eu vejo meu cartão de tempo?" pontos no artigo nome publicado em vez de um que já não existe. lib/titles Títulos de perguntas aos publicados. Como fazer exame de uma regra; o 53 conceitos são nomeados à mão, porque uma regra que poderia virar diferença entre Status e Sub-Status?" em um nome de página teria mais Excepções do que casos. Slugs são verificados para colisões em todo o árvore antes de uma importação correr -- o banco de dados os restringe por organização, não por mãe. relações de compilação O gráfico see- also, construído não direcionado porque links são simétrico no produto. 594 links, 3 a 6 por artigo. Sementes de rascunhos próprios relacionados: matéria frontal, que alguém escolheu durante a escrita; Em cima da posição na árvore. A semelhança de texto classifica os candidatos, mas nunca qualifica um por si só -- "Notas" e "Como pesquisar e filtrar seu Leads" compartilhar cada palavra sobre pesquisa e não tem nada a ver com cada outro. Nenhuma página está relacionada com seu próprio pai ou filho: uma seção já lista seus artigos, e um artigo já tem migalhas de pão. 57 testes, e lista de inclusão do vitest cresce uma linha então eles realmente funcionam.

  19. · kamo-shared-library

    Um mercado decide quem cita nomes como contato do cliente

    QuoteContactMode dá a um LeadMarket três respostas: o agente atribuído ao lead (resolvido por leitura, que é o que cada citação faz hoje e o que uma coluna nula significa), uma pessoa explícita, ou uma escolha feita em cada citação. Os dois últimos são snapshotted on Quote.contactMemberId quando a citação é levantada, então mudando um mente do mercado nunca reescreve o contato que um cliente já foi dado, e um revisão leva o instantâneo para frente — é a mesma oferta. QuoteService toma o contato resolvido como um argumento em vez de lê-lo off the request body: este módulo atinge o lead somente por soft id, e um valor Um cliente poderia deixar qualquer um ser nomeado em um documento. Actualização honra-o somente quando o chamador passa um, que é como ADJUSTABLE PER QUOTE é forçado server-side em vez de pelo editor que esconde um campo. agentForMember é nomeado em vez de sobrecarregado — agentFor(null, ...) seria ambíguo em qualquer local de chamada que passe um nulo literal — e a forma de tomada de dianteira Delega agora a ele, de modo que os dois não podem derivar em um documento impresso.

  20. · InitializerService

    Colunas para o contato de cotação de um mercado e o instantâneo por citação

    Um mercado designa a sua resposta em QUOTE CONTACT MODE (+ QUOTE CONTACT MEMBER ID quando que resposta é uma pessoa explícita), e cada citação mantém o contato que foi Criada com o ******************* Ambos nulláveis sem padrão: um modo nulo lê como o agente atribuído e um instantâneo nulo cai para ele, então cada mercado e cotação existentes é inalterado até que alguém escolhe o contrário.

  21. · kamo-internal

    Curar artigos relacionados com um artigo

    KbRelated Painel é um componente em três lugares: é curador no compositor e o editor, e lê novamente sobre a visualização do artigo. Adicionando um artigo aqui também adiciona este a esse artigo, e o painel diz assim -- caso contrário o segunda metade da escrita parece algo que edita um artigo não deve Já o fiz. Dois modos, porque links podem ser escolhidos antes do artigo existir. Com guid o painel possui seu estado e escreve cada mudança diretamente através: uma reordenação perdido para um Save esquecido é uma reordenação que ninguém faz duas vezes. Sem um, o compositor detém a seleção e coloca-a depois de criar, como uma chamada separada para a mesma razão submit-para-review é um -- um link que não consegue salvar deve Deixe o artigo escrito para trás em vez de derrubá-lo com ele. Sugestões vêm do servidor, que classifica irmãos à frente de artigos que apenas compartilhar vocabulário, e filtra-los através do mesmo escopo leitor que qualquer outra leitura. O painel mantém a resposta do servidor em vez da lista que enviou, assim, um guid do servidor deixado cair não é mostrado como salvo.

  22. · KBService

    Artigos relacionados, e um caminho público que mantém a árvore

    Três coisas, todas na mesma página. Artigos relacionados. KbArticleRelationService possui o "ver também" simétrico links: list, set, sugestion, e um claro que é executado no soft delete para que um aposentado O artigo não permanece na lista de outra pessoa e o encurta silenciosamente. As leituras passam pelo KbReaderScope -- uma lista de relações não filtrada nomes dos títulos de exatamente os artigos que as bandeiras do público escondem, que é o oráculo readable() responde 404 para evitar, chegando através de uma porta diferente. O público ler é ainda mais estreito: publicado e público-visível apenas, então uma página de marketing não pode vincular um visitante em material somente para membros. Caminho público. KbPublicArticleDTO agora carrega caminho e antepassados. caminho é o cadeia de lesma abaixo da raiz da seção, por isso /help-center/help-center é não representável e um consumidor pode construir um link aninhado em vez de um plano. Isto não é cosmético: pesquisa de artigo resolve no último segmento de caminho, então um link construído a partir da lesma nua ainda renderiza a página direita e os perdidos A hierarquia não aparece em lado nenhum até alguém ler o mapa do site. Uma leitura em vez de quatro. buildPublicTreeIndex substitui o separado buildUidToGuidMap / buildPublicChildrenMap scans -- uma única página de artigo foi fazendo três leituras de tabela completa mais um quarto para seu pai, todas as projeções de A mesma lista. Breadcrumbs e cartões relacionados agora traduzir com o artigo em vez de permanecer em inglês debaixo de um corpo traduzido. 98 testes, 30 deles novos: forma do caminho e ciclos em Kb PublicPathTest, espelho manutenção e filtragem de escopo em KbArtRelationServiceTest.

  23. · InitializerService

    Migração para KB ARTICLE RELAÇÕES

    Espelhos KbArticleRelation in kamo-shared-library. Escrito explicitamente em vez de do que a esquerda para ddl-auto assim que o UID e DATE CREATED predefinições viver no banco de dados, que é o que permite uma simples inserção SQL funcionar sem nomeá-los. O índice único está no par ordenado, não no não ordenado: (A,B) e (B,A) ambos são esperados para existir, e restringir o par não ordenado seria rejeitar a linha do espelho que o serviço escreve em cada link. Já aplicado à produção executando estas instruções como o aplicativo kamo user; eles são idempotent, então uma execução completa posterior do KI é um no-op.

  24. · kamo-shared-library

    Relações entre artigos e artigos

    KB ARTICLE RELAÇÕES mantém os links "ver também" curados entre artigos em uma base de conhecimentos. As linhas são por direção e o par é mantido simétrico por KbArticleRelationService, que é a única coisa permitida para escrevê-los -- um par semi- escrito mostra o link em um artigo e não o outro, e é invisível do lado que ainda tem sua fileira. Duas linhas em vez de uma para que cada direcção tenha a sua própria SORT ORDER: uma artigo ordena a lista sob ele sem perturbar o outro fim, e cada ler permanece uma única pesquisa indexada no ARTIGO ID. A DDL já aterrou. KamoInitializerService, aplicado ao Yugabyte como usuário do aplicativo kamo).

  25. · SecurityService

    Página e pesquisa o diretório org, e pára dois construtores de linhas à deriva

    Os terminais de rede respondem com todo o conjunto em um corpo. Isso é bom para um membro de uma dúzia de orgs e impossível para um operador em uma propriedade destinada a alcançar Mostrar vinte. Adiciona GET /org/network-directório, que corresponde, pedidos e janelas no banco de dados e retorna uma página. scope=all é fechado em exatamente a verificação todas as redes se aplica — SYSTEM USER através PlatformRightsService — portanto, as três rotas para a direita continuam trabalhando e a lista Nunca poderá ser maior do que a ação por trás disso. A mesma resposta volta como canEnterAsSystemMember, então um cliente constrói suas linhas e botões a partir de uma resposta. my-redes e todas-redes tinham um construtor de linhas escrito à mão cada, e as cópias tinham já à deriva: todas as redes nunca emitiu primárioColor, logoUrl ou logoSimpleUrl, assim /network desenhou cada org um operador não era um membro do console padrão sotaque em vez do próprio org. Nada o relatou — as chaves estavam simplesmente ausentes E o retorno do cliente fez o resto. Todos os três agora compartilham OrgNetworkRowBuilder.

  26. · kamo-marketing

    Base inicial até $10 em cada termo

    69 / 59 / 49, de 59 / 49 / 39. A escada de compromisso não é tocada pelo design — ainda exatamente $10 de desconto para um ano e $20 para três — que é o padrão cada superfície agora afirma em dólares em vez de como uma porcentagem. Fallbacks em cinco arquivos mover com ele, e check-price-tokens aposenta o figuras substituídas para que ninguém cole um preço inicial antigo de volta em uma comparação Page. As três descrições de metadados citaram "$39/seat/mo" como o preço a partir do número que um resultado de pesquisa mostra é uma reivindicação de preço como qualquer outro. O comentário do pacote em PreciseClient mantém suas figuras de $53/$59, mas agora está datado: eles são a razão para uma decisão passada, não uma citação viva, e um velho preço em um comentário lê como um bug para a próxima pessoa. Verificado contra o catálogo ao vivo em vez dos retrocessos: Starter's three-year renderizações de assento Era $49 / Agora $32.83, ea tira homepage concorda com /pricing.

  27. · kamo-internal

    A oferta em execução, em Planos e faturamento e na plataforma de lançamento

    O console citado lista preços enquanto kamocrm.com anunciou um terceiro fora do mesmo plano, porque nada no kamo-interno podia ver que uma oferta existia. Agora. lê a oferta resolvida do BillingService e mostra o que faz ao preço: um banner acima do selecionador plano com uma contagem regressiva e o que ele salva na ordem em mão, e cada cartão plano coberto atingiu através de sua taxa autorizada para o Ofereceu um. A estimativa abaixo já preços o que o cartão mostra, então os dois não posso discordar. A plataforma de lançamento ganha uma pílula de uma linha ao lado da pílula experimental — à esquerda, e mostrado se a pílula experimental está ou não em cima, uma vez que uma organização passado seu julgamento sem assinatura é exatamente para quem a oferta é. Está fechado em ser capaz de agir sobre o: um membro que pode comprar o seu próprio lugar, ou um proprietário que pode comprar o organização, e que já não está pagando. Esse último teste é lido Cover.source, não cover.payBy — pay by diz WHO cobre o assento, e alguém sentado na assinatura livre da organização lê ORGANIZAÇÃO enquanto estiver em nenhum plano pago em tudo. Oferecendo uma atualização para alguém já pagar é a maneira mais rápida de tornar um banner invisível. planOffer.ts detém a aritmética para que a pílula ea consola não pode derivar, e porque estes são números em que as pessoas gastam dinheiro: a porcentagem sai da taxa para o termo COMPROMETIDO a, nunca o preço de lista mês-a-mês, correspondente **************************** 18 ensaios abrangem essa ordenação, o banco e scaling termo, que um plano descoberto é deixado sozinho, e que a data final é o No último dia, o código funciona em vez do primeiro dia em que não funciona. check-server- timestamps capted the expension parse: `new Date(\`${d}T23:59:59Z\`)` is UTC explícito e correto, mas o guarda não pode dizer isso do bug de texto ingênuo existe para apanhar, e um guarda pessoas aprendem a discutir com é pior do que o inseto. Ele passa pelo parseServerDate agora, que mantém um valor apenas de data na meia-noite UTC — portanto, o prazo é esse mais um dia, o que é também mais claro sobre o porquê.

  28. · BillingService

    Servir a oferta de corrida, e dizer se um assento é realmente PAID para

    Adiciona GET /markets/{id}/promoção. Não havia nenhuma maneira de uma superfície de compra no aplicativo para saber que uma oferta estava em andamento — o site de marketing leu promoções através do proxy de catálogo público, e kamo-internal não tinha equivalente, então os Planos & faturamento console citado list prices enquanto kamocrm.com anunciou um terceiro fora do mesmo plano. Retorna a oferta RESOLVED, não a linha crua. Cada consumidor da forma bruta teve que re-derir as mesmas três coisas — se é vivo hoje, que planos cobre por código e não por UUID, e que ciclos — e cada um que fez Então, pegou pelo menos um errado: o site de marketing ampliou um código de dois planos em um código de todos os planos, comparando UUIDs com códigos de plano, e seu herói filtrado em o desconto TIPO e silenciosamente não anunciou nada durante meses. A vida vem de SubscriptionPromotions.check, o mesmo predicado calculaPreço se aplica quando realmente descontos uma ordem, assim que uma superfície não pode anunciar uma verificação de código Decodificador. Os planos voltam em ordem de exibição, porque as superfícies sentença e "Business and Starter" lê como se as camadas correram para baixo. /self/coverage agora também retorna `source`. Nomes «pagos por» Quem cobre um lugar, que é uma questão diferente de saber se alguma coisa está a ser paga por ele: um membro em a assinatura free-default da organização lê pagaPor = ORGANIZAÇÃO e está em Nenhum plano pago. Qualquer coisa que ofereça uma atualização tem que ser capaz de dizer a esses separado, e pago por não pode.

  29. · kamo-marketing

    A oferta atinge o herói ea faixa de preços

    O herói já tinha uma fita de contagem regressiva, e não tinha nada desde então. o catálogo foi movido para uma porcentagem: `pickHeroOffer` filtrado em FLAT PER SEAT porque a cópia do banner era "$18, cada mês por 12 meses". Um segundo, definição mais estreita de "que oferta está em execução" foi todo o problema, por isso é ido — o herói, a página de preços, a página de oferta, a calculadora e agora o homepage strip todos ler o um `PagePromotion` resoluçãoPromotion retorna. A fita é re-keyed para a porcentagem, links para /pricing / oferta, e contagens até o fim do último dia (meia-noite correu um dia antes). Ele carrega o ember acentuar a oferta desgasta em cada outra superfície, por isso é reconhecível como o promoção antes de uma palavra dele é lido; sua tinta CTA é medida contra isso gradiente em vez de branco esquerdo, que está abaixo de 4.5:1 em ambas as extremidades. A tira homepage agora renderiza o mesmo componente PlanPrice /pricing faz — foi, Agora, e o que você salvar — em vez de sua própria escada laminada à mão sobre anualPreço. Ambas abertas no HOME TERM, nomeadas uma vez em plan-pricing.ts porque três superfícies foram padrão independentemente, e os CTAs da tira carregam o código nos planos capas de oferta. IDs de plano em uma promoção resolvida agora voltar em ordem CATALOG. A API retorna eles em ordem de consulta — Negócios antes de Starter — eo herói escreve-los em um sentença, para que ele leia "em cada assento de Negócios e Iniciante", como se as camadas Correu colina abaixo. check-i18n-keys não pôde ver Hero.tsx AT ALL: ele liga `const [t, slideshow] = E o guarda só entendeu «const t = ...». Três chaves em falta no componente mais lido do site, invisível para o guarda cujo propósito inteiro é capturar um caminho chave bruto renderizando como visível Mensagem. Entende agora a forma desestruturada e — o mais importante — um ficheiro que traduz claramente, mas não produz nenhum espaço de nomes é informado em vez de ignorado, assim, a próxima nova forma de ligação custa uma atualização guarda em vez de transporte "home.heroOfferCta" no herói. A cobertura passou de 1044 para 1073. check-plan-pricing cresce para 55 verificações, cobrindo como os planos de uma oferta são escritos fora e resolverPromoção contra a forma que a API realmente retorna — incluindo que uma promoção cujos planos não resolvem seja recusada em vez de alargada. Verificado contra o catálogo ao vivo em vez do retorno: homepage e /pricing ambos renderizam $0 / $26.13 / $66.33 / Custom.

  30. · kamo-marketing

    Um trilho de compromisso de três prazos, e uma promoção a página pode provar

    Preços novos: Inicio $59/$49/$39 and Business $119/$109/$99 por assento por mês on Mensal, anual e trienal. O trilho substitui o par mensal/anual e sua pílula "Save 18%" — uma porcentagem foi a unidade errada para um apartamento de $10/$20 por assento, então a pílula citado qualquer plano `encontrar ` aconteceu para retornar primeiro, e um a opção não pode expressar três opções. Seu polegar é um cadeado que fecha como o termo alonga, que é a legenda acima dele ("Tranca na tua taxa hoje!") feito literal. A banda da oferta declara a promoção em execução uma vez, acima das cartas, e diz que é aplicado automaticamente. Isso é verdade em vez de decorativo: o plano CTAs agora ?code=, que o RegisterFlow já lê em sp checkout code, e apenas em planos que a oferta cobre realmente — enviar AUTUMN para checkout a partir do Free ou O cartão empresarial promete um desconto no servidor e depois descodificadores. Starter e mostra cartões de visita Foi / Agora / o que você salvar, ea promoção vem a taxa Term, não o preço de lista — commit first, em seguida, retirar a percentagem O número já reduzido, combinando com o número de telefone. Quatro superfícies estavam cada uma fazendo esta aritmética e discordando, então ele move-se para app/lib/plan-pricing.ts e todos eles lêem a mesma resposta. A a percentagem da calculadora promo foi carregar contra add-ons, caixas de correio, extensões e armazenamento, bem como assentos, então sua estimativa saiu abaixo do que o servidor contas; agora é escopo para os assentos plano como o do servidor. /pricing / oferta é reconstruída em torno de qualquer promoção que o catálogo carrega. Tinha. "18DOFF / $18 off Business" hardcoded in the client E em sua página fallback E através de nove cadeias de dicionário, e o catálogo tinha passado por todos os três. Além disso: package-lock.json estava fora de sincronia com package.json (@swc/helpers faltando), então `npm ci' — que é o que o Dockerfile executa — falhou no main. A compilação da imagem foi quebrado independentemente deste trabalho.

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