Compare commits
4 Commits
c7f31231ae
...
446f023425
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
446f023425 | ||
|
|
d7285882ec | ||
|
|
ed940251e2 | ||
|
|
b2fb8fd8e3 |
1481
Lançamentos/Script_lançamento_EUD_v2.ipynb
Normal file
1481
Lançamentos/Script_lançamento_EUD_v2.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
1468
Lançamentos/Script_lançamento_boti_v2.ipynb
Normal file
1468
Lançamentos/Script_lançamento_boti_v2.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
1644
promoção/promoção_EUD_ciclo07.ipynb
Normal file
1644
promoção/promoção_EUD_ciclo07.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
2279
promoção/promoção_boti_ciclo07.ipynb
Normal file
2279
promoção/promoção_boti_ciclo07.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
234
relatório_improdutivo/teste email excel.py
Normal file
234
relatório_improdutivo/teste email excel.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
# enviar_email_excel.py
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
import ssl
|
||||||
|
import pyodbc
|
||||||
|
import configparser
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.ticker as mtick
|
||||||
|
import seaborn as sns
|
||||||
|
from email.message import EmailMessage
|
||||||
|
from email.utils import make_msgid
|
||||||
|
from email.mime.image import MIMEImage
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime, time
|
||||||
|
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(r"C:\Users\joao.herculano\Documents\Enviador de email\credenciais.ini")
|
||||||
|
|
||||||
|
print(config['banco']['host'],config['banco']['user'],config['banco']['password'])
|
||||||
|
|
||||||
|
# Conexão com o banco
|
||||||
|
conn = pyodbc.connect(
|
||||||
|
f"DRIVER={{SQL Server}};"
|
||||||
|
f"SERVER={config['banco']['host']},1433;"
|
||||||
|
f"DATABASE=GINSENG;"
|
||||||
|
f"UID={config['banco']['user']};"
|
||||||
|
f"PWD={config['banco']['password']}")
|
||||||
|
|
||||||
|
# 1. Criar dados fictícios e gerar Excel
|
||||||
|
query = '''
|
||||||
|
select
|
||||||
|
*,
|
||||||
|
CASE
|
||||||
|
WHEN dayswithoutsales BETWEEN 40 AND 59 THEN 'mais de 40 dias'
|
||||||
|
WHEN dayswithoutsales BETWEEN 60 AND 79 THEN 'mais de 60 dias'
|
||||||
|
WHEN dayswithoutsales BETWEEN 80 AND 99 THEN 'mais de 80 dias'
|
||||||
|
WHEN dayswithoutsales >= 100 THEN 'acima de 100 dias'
|
||||||
|
ELSE 'menos de 40 dias'
|
||||||
|
end as status_venda,
|
||||||
|
pricesellin * (stock_actual + stock_intransit) AS valor_estoque_parado
|
||||||
|
FROM Draft
|
||||||
|
where dayswithoutsales > 40
|
||||||
|
and stock_actual > 0
|
||||||
|
and isproductdeactivated <> 1
|
||||||
|
and currentcyclesales = 0
|
||||||
|
'''
|
||||||
|
df = pd.read_sql(query, conn)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
remetente = config['credenciais']['remetente']
|
||||||
|
senha = config['credenciais']['senha']
|
||||||
|
destinatarios = [email.strip() for email in config['email']['destinatarios'].split(',')]
|
||||||
|
assunto = config['email']['assunto']
|
||||||
|
|
||||||
|
print(remetente,senha,destinatarios,assunto)
|
||||||
|
|
||||||
|
pdvs = pd.read_excel(r"C:\Users\joao.herculano\Documents\PDV_ATT.xlsx")
|
||||||
|
|
||||||
|
df['loja_id'] = df['loja_id'].astype('Int64')
|
||||||
|
pdvs['PDV'] = pdvs['PDV'].astype('Int64')
|
||||||
|
|
||||||
|
|
||||||
|
df2= pd.merge(left=df,right=pdvs[['PDV','UF']],left_on='loja_id',right_on='PDV',how='inner')
|
||||||
|
|
||||||
|
# Dicionário de renomeação
|
||||||
|
colunas_traduzidas = {
|
||||||
|
"loja_id": "id_loja",
|
||||||
|
"code": "código",
|
||||||
|
"description": "descrição",
|
||||||
|
"launch": "lançamento",
|
||||||
|
"deactivation": "desativação",
|
||||||
|
"thirdtolastcyclesales": "venda_terceiro_ciclo_passado",
|
||||||
|
"secondtolastcyclesales": "venda_penúltimo_ciclo",
|
||||||
|
"lastcyclesales": "venda_último_ciclo",
|
||||||
|
"currentcyclesales": "venda_ciclo_atual",
|
||||||
|
"nextcycleprojection": "projeção_próximo_ciclo",
|
||||||
|
"secondtonextcycleprojection": "projeção_segundo_próximo_ciclo",
|
||||||
|
"stock_actual": "estoque_atual",
|
||||||
|
"stock_intransit": "estoque_em_transito",
|
||||||
|
"purchasesuggestion": "sugestao_compra",
|
||||||
|
"smartpurchase_purchasesuggestioncycle": "compra_inteligente_ciclo",
|
||||||
|
"smartpurchase_nextcyclepurchasesuggestion": "compra_inteligente_prox_ciclo",
|
||||||
|
"pendingorder": "pedido_pendente",
|
||||||
|
"salescurve": "curva_vendas",
|
||||||
|
"promotions_description": "descrição_promocao",
|
||||||
|
"promotions_discountpercent": "desconto_promocao_percentual",
|
||||||
|
"pricesellin": "preço_sellin",
|
||||||
|
"businessunit": "unidade_negócio",
|
||||||
|
"codcategory": "código_categoria",
|
||||||
|
"criticalitem_dtprovidedregularization": "dt_regularização_item_critico",
|
||||||
|
"criticalitem_blockedwallet": "carteira_bloqueada_item_critico",
|
||||||
|
"criticalitem_iscritical": "é_item_critico",
|
||||||
|
"codsubcategory": "código_subcategoria",
|
||||||
|
"isproductdeactivated": "produto_desativado",
|
||||||
|
"brandgroupcode": "código_grupo_marca",
|
||||||
|
"dayswithoutsales": "dias_sem_venda",
|
||||||
|
"coveragedays": "dias_cobertura",
|
||||||
|
"hascoverage": "tem_cobertura"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Renomeando as colunas do DataFrame
|
||||||
|
df2 = df2.rename(columns=colunas_traduzidas)
|
||||||
|
|
||||||
|
excel_path = Path("relatorio.xlsx")
|
||||||
|
df2.to_excel(excel_path, index=False)
|
||||||
|
|
||||||
|
|
||||||
|
# 2. Criar e salvar gráfico
|
||||||
|
plot_df = df2.groupby('UF')['valor_estoque_parado'].sum().reset_index()
|
||||||
|
plt.figure(figsize=(6, 4))
|
||||||
|
ax = sns.barplot(data=plot_df, x='UF', y='valor_estoque_parado', errorbar=None)
|
||||||
|
ax.yaxis.set_major_formatter(mtick.StrMethodFormatter('R${x:,.2f}'))
|
||||||
|
for p in ax.patches:
|
||||||
|
valor = p.get_height()
|
||||||
|
ax.annotate(f'R$ {valor:,.2f}', (p.get_x() + p.get_width() / 2, valor),
|
||||||
|
ha='center', va='bottom', fontsize=9)
|
||||||
|
plt.title("Estoque parado por UF")
|
||||||
|
plt.ylabel("Valor em Reais")
|
||||||
|
plt.xlabel("UF")
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig("grafico.png")
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# Obtém a hora atual
|
||||||
|
agora = datetime.now().time()
|
||||||
|
# Define os intervalos de tempo
|
||||||
|
manhã_inicio = time(5, 0)
|
||||||
|
manhã_fim = time(12, 0)
|
||||||
|
tarde_inicio = time(12, 1)
|
||||||
|
tarde_fim = time(18, 0)
|
||||||
|
# noite é dividida em dois intervalos por causa da virada do dia
|
||||||
|
noite_inicio = time(18, 1)
|
||||||
|
noite_fim = time(4, 59)
|
||||||
|
# Verifica em qual intervalo a hora atual está
|
||||||
|
if manhã_inicio <= agora <= manhã_fim:
|
||||||
|
boa = "Bom dia!"
|
||||||
|
elif tarde_inicio <= agora <= tarde_fim:
|
||||||
|
boa = "Boa tarde!"
|
||||||
|
else:
|
||||||
|
boa = "Boa noite!"
|
||||||
|
|
||||||
|
df2['DATA'] = agora
|
||||||
|
|
||||||
|
df3 = df2.groupby('DATA', as_index=False)['valor_estoque_parado'].sum()
|
||||||
|
|
||||||
|
path2 = r'C:\Users\joao.herculano\OneDrive - GRUPO GINSENG\Documentos\acompanhamentos\40D sem Venda\acompanhamento40DSV.xlsx'
|
||||||
|
|
||||||
|
# Tenta abrir e escrever com append
|
||||||
|
with pd.ExcelWriter(path2, mode='a', engine='openpyxl', if_sheet_exists='overlay') as writer:
|
||||||
|
# Encontra a última linha preenchida
|
||||||
|
book = writer.book
|
||||||
|
sheet = writer.sheets['Sheet1'] if 'Sheet1' in writer.sheets else writer.book.active
|
||||||
|
start_row = sheet.max_row
|
||||||
|
|
||||||
|
# Escreve sem cabeçalho se não for a primeira linha
|
||||||
|
df3.to_excel(writer, index=False, header=not start_row > 1, startrow=start_row)
|
||||||
|
|
||||||
|
# 3. Criar e-mail com imagem embutida
|
||||||
|
grafico_cid = make_msgid()[1:-1] # remove < >
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg['From'] = remetente
|
||||||
|
msg['To'] = ', '.join(destinatarios)
|
||||||
|
msg['Subject'] = assunto
|
||||||
|
|
||||||
|
|
||||||
|
# 4. Conteúdo do e-mail
|
||||||
|
html_email = f"""
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>{boa}</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Segue o relatório semanal de estoque improdutivo referente aos estados de
|
||||||
|
Alagoas (AL), Bahia (BA), Sergipe (SE) e região de Vitória da Conquista (VDC).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Este relatório contempla exclusivamente os itens que possuem saldo em estoque,
|
||||||
|
mas que estão sem vendas há mais de 40 dias.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
O objetivo é trazer visibilidade para os produtos parados e reforçar a importância
|
||||||
|
de ações para estimular a sua saída, contribuindo assim para a redução da cobertura
|
||||||
|
de estoque e otimização dos recursos.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Contamos com o apoio de todos para análise e tratativa dos itens listados.
|
||||||
|
Sugestões de ações como campanhas, transferências ou ajustes de sortimento são
|
||||||
|
bem-vindas para acelerar a movimentação dos produtos.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Para mais informações, favor consultar a planilha em anexo.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Segue resumo:</b></p>
|
||||||
|
<img src="cid:{grafico_cid}">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
msg.set_content("Seu e-mail precisa de um visualizador HTML.")
|
||||||
|
msg.add_alternative(html_email, subtype='html')
|
||||||
|
|
||||||
|
# 4. Anexar gráfico inline
|
||||||
|
with open("grafico.png", 'rb') as img:
|
||||||
|
msg.get_payload()[1].add_related(img.read(), 'image', 'png', cid=grafico_cid)
|
||||||
|
|
||||||
|
|
||||||
|
# 5. Anexar o arquivo Excel
|
||||||
|
with open(excel_path, 'rb') as f:
|
||||||
|
msg.add_attachment(
|
||||||
|
f.read(),
|
||||||
|
maintype='application',
|
||||||
|
subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
filename=excel_path.name
|
||||||
|
)
|
||||||
|
|
||||||
|
# 6. Enviar o e-mail via SMTP Outlook com configurações fornecidas
|
||||||
|
with smtplib.SMTP('smtp-mail.outlook.com', 587) as smtp:
|
||||||
|
smtp.ehlo()
|
||||||
|
smtp.starttls(context=ssl.create_default_context())
|
||||||
|
smtp.login(remetente, senha)
|
||||||
|
smtp.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print("E-mail enviado com sucesso.")
|
||||||
221
relatório_ruptura/ruptura projetada 23.05.py
Normal file
221
relatório_ruptura/ruptura projetada 23.05.py
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
import smtplib
|
||||||
|
import ssl
|
||||||
|
import pyodbc
|
||||||
|
import configparser
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
from email.message import EmailMessage
|
||||||
|
from email.utils import make_msgid
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime, time
|
||||||
|
|
||||||
|
from email.mime.image import MIMEImage
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(r"C:\Users\joao.herculano\Documents\Enviador de email\credenciais.ini")
|
||||||
|
|
||||||
|
conn = pyodbc.connect(
|
||||||
|
f"DRIVER={{SQL Server}};"
|
||||||
|
f"SERVER={config['banco']['host']},1433;"
|
||||||
|
f"DATABASE=GINSENG;"
|
||||||
|
f"UID={config['banco']['user']};"
|
||||||
|
f"PWD={config['banco']['password']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
calendario = pd.read_excel(r"C:\Users\joao.herculano\GRUPO GINSENG\Assistência Suprimentos - 2025\SUPRIMENTOS\BD_LANÇAMENTOS\BASE DE DADOS LANÇAMENTO\BOT\CICLO 9\CALENDARIO_CICLO\Ciclo_Expandido_com_Datas.xlsx")
|
||||||
|
calendario.columns = calendario.columns.str.lower()
|
||||||
|
calendario['date'] = pd.to_datetime(calendario['date'])
|
||||||
|
today = pd.Timestamp("today").normalize()
|
||||||
|
calendario = calendario[calendario['marca'] == "BOTICARIO"]
|
||||||
|
calendario['num_ciclo'] = calendario['ciclo'].str[-2:].astype(int)
|
||||||
|
calendario['ano_ciclo'] = calendario['ciclo'].str[0:5]
|
||||||
|
calendario['ciclomais2'] = calendario['ano_ciclo'].astype(str) + (calendario['num_ciclo'] + 0).astype(str).str.zfill(2)
|
||||||
|
ciclo_mais2 = calendario[calendario['date'].dt.normalize() == today]['ciclomais2'].iloc[0]
|
||||||
|
filtered_calendario = calendario[calendario['ciclo'] == ciclo_mais2][:1].copy()
|
||||||
|
filtered_calendario['dias_ate_fim'] = (filtered_calendario['fim ciclo'].iloc[0] - today).days
|
||||||
|
print(filtered_calendario[['duração', 'dias_ate_fim']])
|
||||||
|
|
||||||
|
query = '''
|
||||||
|
SELECT
|
||||||
|
businessunit AS marca,
|
||||||
|
codcategory AS categoria,
|
||||||
|
loja_id AS pdv,
|
||||||
|
code AS sku,
|
||||||
|
description AS descricao_produto,
|
||||||
|
salescurve AS curva,
|
||||||
|
CASE WHEN promotions_description IS NULL THEN 'REGULAR' ELSE 'PROMOÇÃO' END AS tipo_promocao,
|
||||||
|
COALESCE(stock_actual, 0) AS estoque,
|
||||||
|
stock_intransit AS transito,
|
||||||
|
nextcycleprojection AS pv_mar,
|
||||||
|
currentcyclesales AS venda_atual,
|
||||||
|
CASE WHEN criticalitem_iscritical = 0 THEN 'REGULAR' ELSE 'CRITICO' END AS status_item
|
||||||
|
FROM Draft
|
||||||
|
WHERE isproductdeactivated = 0 AND codcategory NOT IN ('SUPORTE A VENDA','EMBALAGENS')
|
||||||
|
'''
|
||||||
|
df = pd.read_sql(query, conn)
|
||||||
|
conn.close()
|
||||||
|
df.columns = df.columns.str.lower()
|
||||||
|
|
||||||
|
filtered_calendario.columns = filtered_calendario.columns.str.lower()
|
||||||
|
df['ddv'] = df['pv_mar'] / filtered_calendario['duração'].values[0]
|
||||||
|
df['estoque_seguranca'] = np.ceil(df['pv_mar'] + (15 * df['ddv'])).astype(int)
|
||||||
|
df['risco_ruptura'] = np.where(df['ddv'] * filtered_calendario['dias_ate_fim'].max() >= df['estoque'], "SIM", "NÃO")
|
||||||
|
df['quantidade_ruptura'] = np.ceil(df['ddv'] * filtered_calendario['dias_ate_fim'].max() - df['estoque'])
|
||||||
|
df['excesso'] = np.where(df['estoque'] - df['estoque_seguranca'] > 0, df['estoque'] - df['estoque_seguranca'], 0)
|
||||||
|
|
||||||
|
remetente = config['credenciais']['remetente']
|
||||||
|
senha = config['credenciais']['senha']
|
||||||
|
destinatarios = [email.strip() for email in config['email_ruptura']['destinatarios'].split(',')]
|
||||||
|
assunto = config['email_ruptura']['assunto']
|
||||||
|
|
||||||
|
df_rpt = pd.read_excel(r"C:\Users\joao.herculano\Downloads\Ruptura Cliente CP GINSENG (1).xlsx")
|
||||||
|
df_rpt.columns = df_rpt.columns.str.lower()
|
||||||
|
|
||||||
|
df['pdv'] = df['pdv'].astype('Int64')
|
||||||
|
df['sku'] = df['sku'].astype('Int64')
|
||||||
|
|
||||||
|
df_rpt['cod_pdv'] = df_rpt['cod_pdv'].astype('Int64')
|
||||||
|
df_rpt['sku1'] = df_rpt['sku1'].astype('Int64')
|
||||||
|
|
||||||
|
df = pd.merge(df, df_rpt[['sku1','cod_pdv','estoque livre?']], left_on=['pdv','sku'], right_on=['cod_pdv','sku1'], how='left')
|
||||||
|
df.drop(columns=['cod_pdv'], inplace=True)
|
||||||
|
|
||||||
|
pdvs = pd.read_excel(r"C:\Users\joao.herculano\Documents\PDV_ATT.xlsx")
|
||||||
|
pdvs.columns = pdvs.columns.str.lower()
|
||||||
|
df['pdv'] = df['pdv'].astype('Int64')
|
||||||
|
pdvs['pdv'] = pdvs['pdv'].astype('Int64')
|
||||||
|
df2 = pd.merge(df, pdvs[['pdv','uf','canal','analista']], on='pdv', how='inner')
|
||||||
|
|
||||||
|
idx = df2.groupby(['uf', 'sku'])['excesso'].idxmax()
|
||||||
|
pdvs_maior_excesso = df2.loc[idx, ['uf', 'sku', 'pdv', 'excesso']].copy()
|
||||||
|
pdvs_maior_excesso.columns = ['uf', 'sku', 'pdv_maior_excesso', 'maior_excesso_por_uf']
|
||||||
|
pdvs_maior_excesso.set_index(['uf', 'sku'], inplace=True)
|
||||||
|
df2 = df2.join(pdvs_maior_excesso, on=['uf', 'sku'])
|
||||||
|
df2['maior excesso na uf'] = df2['maior_excesso_por_uf'].apply(lambda x: 'não tem excesso no uf' if x == 0 else None)
|
||||||
|
df2['maior excesso na uf'] = df2['maior excesso na uf'].combine_first(df2['pdv_maior_excesso'])
|
||||||
|
df2['quantidade_ruptura'] = df2['quantidade_ruptura'].clip(lower=0)
|
||||||
|
print(df2[['uf', 'sku', 'excesso', 'maior_excesso_por_uf', 'maior excesso na uf']].head())
|
||||||
|
df2.drop(columns=['pdv_maior_excesso','sku1'], inplace=True)
|
||||||
|
|
||||||
|
df2['estoque livre?'] = np.where(
|
||||||
|
df2['estoque livre?'].isna() & (df2['status_item'] == 'CRITICO'),
|
||||||
|
'Não',
|
||||||
|
df2['estoque livre?']
|
||||||
|
)
|
||||||
|
|
||||||
|
df2['estoque livre?'] = np.where(
|
||||||
|
df2['estoque livre?'].isna() & (df2['status_item'] == 'REGULAR'),
|
||||||
|
'Sim',
|
||||||
|
df2['estoque livre?']
|
||||||
|
)
|
||||||
|
|
||||||
|
excel_path = Path("relatorio.xlsx")
|
||||||
|
|
||||||
|
colunas_ordenadas = [
|
||||||
|
'uf',
|
||||||
|
'canal',
|
||||||
|
'analista',
|
||||||
|
'marca',
|
||||||
|
'categoria',
|
||||||
|
'pdv',
|
||||||
|
'sku',
|
||||||
|
'descricao_produto',
|
||||||
|
'curva',
|
||||||
|
'tipo_promocao',
|
||||||
|
'estoque',
|
||||||
|
'transito',
|
||||||
|
'pv_mar',
|
||||||
|
'venda_atual',
|
||||||
|
'status_item',
|
||||||
|
'ddv',
|
||||||
|
'estoque_seguranca',
|
||||||
|
'risco_ruptura',
|
||||||
|
'quantidade_ruptura',
|
||||||
|
'excesso',
|
||||||
|
'estoque livre?',
|
||||||
|
'maior_excesso_por_uf',
|
||||||
|
'maior excesso na uf'
|
||||||
|
]
|
||||||
|
|
||||||
|
df2 = df2[colunas_ordenadas]
|
||||||
|
|
||||||
|
df3 = df2[df2['canal'] != "LJ"]
|
||||||
|
|
||||||
|
df3 = df3.groupby(['uf', 'canal','pdv'])['quantidade_ruptura'].sum().sort_values(ascending=False).reset_index()
|
||||||
|
|
||||||
|
with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
|
||||||
|
df2.to_excel(writer, sheet_name='Detalhado', index=False)
|
||||||
|
df3.to_excel(writer, sheet_name='Resumo', index=False)
|
||||||
|
|
||||||
|
ruptura_total = df2['quantidade_ruptura'].sum()
|
||||||
|
ruptura_por_uf_pct = (
|
||||||
|
df2.groupby('uf')['quantidade_ruptura'].sum()
|
||||||
|
.sort_values(ascending=True)
|
||||||
|
.apply(lambda x: (x / ruptura_total) * 100)
|
||||||
|
)
|
||||||
|
print(ruptura_por_uf_pct)
|
||||||
|
ax = ruptura_por_uf_pct.plot(kind='barh', figsize=(10, 6), color='skyblue')
|
||||||
|
for i, v in enumerate(ruptura_por_uf_pct):
|
||||||
|
ax.text(v + 0.3, i, f"{v:.1f}%", va='center')
|
||||||
|
plt.xlabel('% da ruptura total')
|
||||||
|
plt.title('Distribuição percentual de ruptura projetada por UF')
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig("grafico.png")
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
agora = datetime.now().time()
|
||||||
|
if time(5, 0) <= agora <= time(12, 0):
|
||||||
|
boa = "Bom dia!"
|
||||||
|
elif time(12, 1) <= agora <= time(18, 0):
|
||||||
|
boa = "Boa tarde!"
|
||||||
|
else:
|
||||||
|
boa = "Boa noite!"
|
||||||
|
|
||||||
|
grafico_cid = make_msgid()[1:-1]
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg['From'] = remetente
|
||||||
|
msg['To'] = ', '.join(destinatarios)
|
||||||
|
msg['Subject'] = assunto
|
||||||
|
html_email = f"""
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>{boa}</p>
|
||||||
|
<p>Compartilhamos o relatório de ruptura projetada, com o objetivo de monitorar os itens com maior risco de ruptura nos próximos dias.</p>
|
||||||
|
<p>O relatório a seguir apresenta as seguintes informações:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Estoque atual;</li>
|
||||||
|
<li>Trânsito;</li>
|
||||||
|
<li>Ruptura projetada;</li>
|
||||||
|
<li>Maior excesso por estado, assim como o PDV que o possui.</li>
|
||||||
|
</ul>
|
||||||
|
<p>Além disso, o material destaca as VDs com maior criticidade, permitindo uma atuação direcionada para mitigar impactos e priorizar ações de abastecimento e transferência.</p>
|
||||||
|
<p><strong>Importante:</strong> O relatório está em processo de desenvolvimento e pode sofrer mudanças futuras no layout. Ficamos à disposição para esclarecer quaisquer dúvidas.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<img src="cid:{grafico_cid}">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
msg.set_content("Seu e-mail precisa de um visualizador HTML.")
|
||||||
|
msg.add_alternative(html_email, subtype='html')
|
||||||
|
|
||||||
|
with open("grafico.png", 'rb') as img:
|
||||||
|
msg.get_payload()[1].add_related(img.read(), 'image', 'png', cid=grafico_cid)
|
||||||
|
|
||||||
|
with open(excel_path, 'rb') as f:
|
||||||
|
msg.add_attachment(
|
||||||
|
f.read(),
|
||||||
|
maintype='application',
|
||||||
|
subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
filename=excel_path.name
|
||||||
|
)
|
||||||
|
|
||||||
|
with smtplib.SMTP('smtp-mail.outlook.com', 587) as smtp:
|
||||||
|
smtp.ehlo()
|
||||||
|
smtp.starttls(context=ssl.create_default_context())
|
||||||
|
smtp.login(remetente, senha)
|
||||||
|
smtp.send_message(msg)
|
||||||
|
|
||||||
|
print("E-mail enviado com sucesso.")
|
||||||
Loading…
x
Reference in New Issue
Block a user