from pathlib import Path from reportlab.lib.colors import HexColor from reportlab.lib.enums import TA_LEFT from reportlab.lib.pagesizes import LETTER from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet from reportlab.lib.units import inch from reportlab.platypus import ( BaseDocTemplate, Frame, PageBreak, PageTemplate, Paragraph, Spacer, ) OUTPUT = Path("output/pdf/resumo-alteracoes-ultimas-duas-semanas.pdf") BLUE = HexColor("#2E75B6") NAVY = HexColor("#1F4E79") BLACK = HexColor("#000000") GRAY = HexColor("#666666") def footer(canvas, doc): canvas.saveState() canvas.setFont("Helvetica", 9) canvas.setFillColor(GRAY) canvas.drawRightString(7.45 * inch, 0.47 * inch, "Nexstar Graphs") canvas.restoreState() def make_styles(): styles = getSampleStyleSheet() return { "title": ParagraphStyle( "SummaryTitle", parent=styles["Normal"], fontName="Helvetica-Bold", fontSize=18, leading=22, textColor=BLACK, spaceAfter=13, alignment=TA_LEFT, ), "overview": ParagraphStyle( "Overview", parent=styles["Normal"], fontName="Helvetica-Bold", fontSize=10, leading=13.5, textColor=NAVY, spaceAfter=16, ), "section": ParagraphStyle( "Section", parent=styles["Normal"], fontName="Helvetica-Bold", fontSize=14.2, leading=17.4, textColor=BLUE, spaceBefore=1, spaceAfter=7, ), "bullet": ParagraphStyle( "Bullet", parent=styles["Normal"], fontName="Helvetica", fontSize=9.8, leading=13.1, textColor=BLACK, leftIndent=19, firstLineIndent=-12, spaceAfter=1.8, ), } def bullet(text, styles): return Paragraph(f"•   {text}", styles["bullet"]) def section(title, items, styles): flowables = [Paragraph(title, styles["section"])] flowables.extend(bullet(item, styles) for item in items) flowables.append(Spacer(1, 8)) return flowables def main(): OUTPUT.parent.mkdir(parents=True, exist_ok=True) document = BaseDocTemplate( str(OUTPUT), pagesize=LETTER, leftMargin=0.96 * inch, rightMargin=0.84 * inch, topMargin=1.08 * inch, bottomMargin=0.75 * inch, title="Resumo de Alterações - Nexstar Graphs", author="Nexstar Graphs", ) frame = Frame( document.leftMargin, document.bottomMargin, document.width, document.height, id="content", ) document.addPageTemplates([PageTemplate(id="main", frames=[frame], onPage=footer)]) styles = make_styles() story = [ Paragraph("RESUMO DE ALTERAÇÕES", styles["title"]), Paragraph( "Visão geral: nas últimas duas semanas, o Graphs evoluiu a integração com a Olist, " "o planejamento de corte, a gestão de ordens de produção, suprimentos e a experiência operacional.", styles["overview"], ), ] story.extend( section( "Integração Olist e dados de produção", [ "Implementada conexão OAuth nativa com a API V3 da Olist, com armazenamento seguro de token, renovação automática e reconexão pela tela de monitoramento.", "Criada sincronização de produtos fabricados e composições, com atualização incremental, retomada após reinício e limite de requisições compatível com a operação.", "Adicionado agendamento diário às 11h, com exibição da próxima execução, progresso, duração, histórico, logs por produto e falhas na tela Olist.", "Incluídos controles para parar e continuar a sincronização e importação de OPs finalizadas por CSV, com deduplicação em reimportações.", "As observações das OPs agora fornecem fornecedor, lote, bobinas, quilos de malha e ribana e rendimento; as pendências podem ser exportadas pela tela Olist.", ], styles, ) ) story.extend( section( "Plano de corte e rendimentos", [ "Reorganizada a tela de Plano de Corte para priorizar produto, necessidade de corte, demanda, disponível e situação operacional, sem repetir indicadores.", "Incluídas edição contextual por SKU, visualização de produto e paginação no plano e na prévia de OPs.", "Centralizada a configuração de corte e simplificado o preenchimento de rendimentos por família.", "Corrigida a taxonomia: BLCS, BLOS, BLMC, BLPM e BLRM representam camiseta regular, oversize, moletom, pima e regata.", "Importado histórico de OPs finalizadas para sugerir rendimento por família e, quando disponível, por fornecedor de tecido.", ], styles, ) ) story.extend( section( "Ordens de produção", [ "Melhorada a prévia das OPs com paginação e seleção de itens individuais, página atual, todos os elegíveis ou somente itens com dados completos.", "Reestruturada a página de Ordens de Produção para usar detalhes expansíveis na tabela, evitando painel lateral e duplicidade de controles.", "Transformado o status em um menu compacto com opções claras para Em aberto, Em andamento, Finalizada e Cancelada.", "Adicionada ação para devolver uma OP local em aberto ao Plano de Corte, preservando o histórico e devolvendo a necessidade ao planejamento.", ], styles, ) ) story.append(PageBreak()) story.extend( section( "Suprimentos, compras e estoque", [ "Redesenhado o hub de Suprimentos para seguir o fluxo Necessidade de Compra, Plano de Corte e Ordens de Produção, com foco nas próximas ações.", "Simplificado o fluxo de Necessidade de Compra: removida a área de mapeamento separada e incluída edição direta dos materiais por ícone.", "Materiais sem saldo, com saldo negativo ou com necessidade de serviço passaram a usar filtros e estados visuais consistentes entre abas.", "Criado centro de exceções para saldos negativos, SKUs sem composição e cadastro para corte, mantendo essas filas informativas e auditáveis.", "Aplicada regra de estoque disponível: saldo negativo vale zero para compras e corte, sem esconder o saldo original para auditoria.", ], styles, ) ) story.extend( section( "Cadastros e produtos", [ "Adicionada paginação com 10 itens por padrão nas listas de Produtos, Categorias e Referências de Consumo.", "Incluídas ações de edição para Categorias, Produtos e Referências de Consumo, aproximando Cadastros da página de Produtos.", "Melhorados os detalhes de composição e a navegação entre variações de produto para tornar a consulta mais direta.", "Criadas ações compactas por ícone para materiais, preservando a leitura das tabelas.", ], styles, ) ) story.extend( section( "Experiência e navegação", [ "Padronizado o visual de Suprimentos, Corte e Produção com as tabelas, filtros, badges e paginação já usados em Produtos.", "Ajustada a cor da família BLCS no Plano de Corte e removidos cartões-resumo redundantes de Vendedores.", "Aprimorada a navegação para dispositivos menores e refinados os estados de carregamento nas páginas comerciais.", ], styles, ) ) document.build(story) print(OUTPUT) if __name__ == "__main__": main()