diff --git a/Lançamentos/Script_lançamento_EUD_v2.ipynb b/Lançamentos/Script_lançamento_EUD_v2.ipynb
new file mode 100644
index 0000000..eecaaea
--- /dev/null
+++ b/Lançamentos/Script_lançamento_EUD_v2.ipynb
@@ -0,0 +1,1481 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "6ad35669",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "import numpy as np \n",
+ "import glob\n",
+ "import os "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "9fcdc77a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "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\")\n",
+ "\n",
+ "calendario['Date'] = pd.to_datetime(calendario['Date'])\n",
+ "\n",
+ "# Get today (normalized to midnight)\n",
+ "today = pd.Timestamp(\"today\").normalize()\n",
+ "\n",
+ "calendario['NUM_CICLO'] = calendario['Ciclo'].str[-2:].astype(int)\n",
+ "\n",
+ "calendario['ANO_CICLO'] = calendario['Ciclo'].str[0:5]\n",
+ "\n",
+ "calendario = calendario[calendario['MARCA'] == \"EUDORA\"]\n",
+ "\n",
+ "calendario['CICLOMAIS2'] = calendario['ANO_CICLO'].astype(str) + (calendario['NUM_CICLO'].astype(int) + 3).astype(str).str.zfill(2) #<<< MUDAR O \"4\" (CICLO ATUAL + 4 PARA ACHAR O CICLO DA SUGESTÃO) EX: C202505 -> C202509\n",
+ "ciclo_mais2 = calendario[calendario['Date'].dt.normalize() == today]['CICLOMAIS2'].iloc[0]\n",
+ "\n",
+ "# Filter rows where date matches today\n",
+ "filtered_calendario = calendario[calendario['Ciclo'] == ciclo_mais2][:1]\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['INICIO CICLO'].iloc[0] - today\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['dias_ate_inicio'].dt.days.astype(int)\n",
+ "\n",
+ "filtered_calendario['match'] = 1\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "bbec229d",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Ciclo | \n",
+ " INICIO CICLO | \n",
+ " FIM CICLO | \n",
+ " DURAÇÃO | \n",
+ " MARCA | \n",
+ " Date | \n",
+ " NUM_CICLO | \n",
+ " ANO_CICLO | \n",
+ " CICLOMAIS2 | \n",
+ " dias_ate_inicio | \n",
+ " match | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2262 | \n",
+ " C202511 | \n",
+ " 2025-07-16 | \n",
+ " 2025-08-05 | \n",
+ " 21 | \n",
+ " EUDORA | \n",
+ " 2025-07-16 | \n",
+ " 11 | \n",
+ " C2025 | \n",
+ " C202514 | \n",
+ " 54 | \n",
+ " 1 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Ciclo INICIO CICLO FIM CICLO DURAÇÃO MARCA Date NUM_CICLO \\\n",
+ "2262 C202511 2025-07-16 2025-08-05 21 EUDORA 2025-07-16 11 \n",
+ "\n",
+ " ANO_CICLO CICLOMAIS2 dias_ate_inicio match \n",
+ "2262 C2025 C202514 54 1 "
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "filtered_calendario"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "61ffc777",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\EUDORA\\C11\\arquivos usados na sugestão\\SIMILARES\\PRODUTOS SIMILARES - EUD.xlsx\")\n",
+ "\n",
+ "df_similares = pd.merge(left=df_similares,right=calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO']], how= 'left', left_on = 'CICLO SIMILAR',right_on = 'Ciclo' )\n",
+ "\n",
+ "df_similares = df_similares.drop(columns=['Ciclo'])\n",
+ "\n",
+ "df_similares = df_similares.rename(columns={'INICIO CICLO':'INICIO CICLO SIMILAR','FIM CICLO':'FIM CICLO SIMILAR','DURAÇÃO':'DURAÇÃO CICLO SIMILAR'})\n",
+ "df_similares.drop_duplicates(inplace=True)\n",
+ "\n",
+ "df_similares['MATCH'] = 1\n",
+ "\n",
+ "df_similares = df_similares.drop(columns=['INICIO DO CICLO',\n",
+ " 'FIM DO CICLO', 'DURAÇÃO CICLO','INICIO CICLO SIMILAR','FIM CICLO SIMILAR','DURAÇÃO CICLO SIMILAR'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "99ea95e6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PRODUTO LANÇAMENTO', 'DESCRIÇÃO DO LANÇAMENTO', 'PRODUTO SIMILAR',\n",
+ " 'DESCRIÇÃO SIMILAR', 'CICLO SIMILAR', 'FOCO', 'IAF', 'CATEGORIA',\n",
+ " 'MARCA', '% CONSUMIDOR', 'MECANICA CONSUMIDOR', '% REVENDEDOR',\n",
+ " 'MECANICA REVENDEDOR', 'TIPO DE PRODUTO', 'MATCH'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_similares.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "fe922f62",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\EUDORA\\C11\\arquivos usados na sugestão\\TABELA DE PEDIDO\\Pedidos Semanais Especiais - GKD - 202511.xlsx\")\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Região'] == 'NNE'] \n",
+ "\n",
+ "df_tabela = df_tabela[(df_tabela['Canal'] != 'Ecomm') | (df_tabela['Canal'] != 'Ecomm | VD') | (df_tabela['Canal'] != 'Ecomm | Loja')] \n",
+ "\n",
+ "df_tabela['Canal'] = np.where((df_tabela['Canal'] == \"Loja\") | (df_tabela['Canal'] == \"Todos\") | (df_tabela['Canal'] == \"Loja | VD\"),\"TODOS\",\"VD\")\n",
+ "\n",
+ "#df_tabela = df_tabela[df_tabela['Tipo de promoção'].str.contains('Lançamentos', na=False)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "a3a045d9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\EUDORA\\C11\\arquivos usados na sugestão\\PDVS EUD\\PDV_ATT.xlsx\")\n",
+ "\n",
+ "df_pdv_origi = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\EUDORA\\C11\\arquivos usados na sugestão\\PDVS EUD\\PDV_ATT.xlsx\")\n",
+ "\n",
+ "df_pdv = df_pdv.rename(columns={'DESCRIÇÃO':'DESCRIÇÃO PDV'})\n",
+ "\n",
+ "df_pdv = df_pdv.drop(columns=['REGIÃO', 'ESTADO','CIDADE','GESTÃO','MARCA'])\n",
+ "\n",
+ "df_pdv['PDV'] = df_pdv['PDV DESC'].str.split(\"-\").str[0].str.strip()\n",
+ "\n",
+ "df_pdv['UF'] = np.where(df_pdv['UF'] == 'VDC','BA',df_pdv['UF'])\n",
+ "\n",
+ "#ignorando a PDV que ainda não está online\n",
+ "df_pdv = df_pdv[df_pdv['DESCRIÇÃO PDV'] != '23813-COMERCIO-HIB VALENTE']\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['status'] != \"INATIVO\"]\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['status'] != \"MATRIZ\"]\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['SUPERVISOR'] != 'Inativa']\n",
+ "\n",
+ "df_pdv['MATCH'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "849d5297",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PDV', 'CANAL', 'DESCRIÇÃO PDV', 'PDV DESC', 'UF', 'ANALISTA',\n",
+ " 'SUPERVISOR', 'status', 'MATCH'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_pdv.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "df04a501",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares = pd.merge(left=df_similares,right=df_pdv,right_on=['MATCH'],left_on=['MATCH'],how='inner')\n",
+ "\n",
+ "df_similares = df_similares.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "0da911af",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\joao.herculano\\AppData\\Local\\Temp\\ipykernel_61796\\3284054138.py:10: DtypeWarning: Columns (7) have mixed types. Specify dtype option on import or set low_memory=False.\n",
+ " df_draft = pd.concat([pd.read_csv(file) for file in csv_files], ignore_index=True)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "(111818, 47)"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Caminho onde estão as subpastas com os arquivos CSV\n",
+ "\n",
+ "# Set the path to the folder containing CSV files\n",
+ "folder_path = r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\EUDORA\\C11\\arquivos usados na sugestão\\DRAFT\" # arquivo dos drafts\n",
+ "\n",
+ "# Pattern to match all CSV files\n",
+ "csv_files = glob.glob(os.path.join(folder_path, '*.csv'))\n",
+ "\n",
+ "# Read and concat all CSVs\n",
+ "df_draft = pd.concat([pd.read_csv(file) for file in csv_files], ignore_index=True)\n",
+ "\n",
+ "df_draft['match'] = 1 \n",
+ "\n",
+ "df_draft.shape\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "0c8c7493",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft = df_draft.drop(columns=['Categoria'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "91298cde",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['Histórico de Vendas do Ciclo 202407',\n",
+ " 'Histórico de Vendas do Ciclo 202408',\n",
+ " 'Histórico de Vendas do Ciclo 202409',\n",
+ " 'Histórico de Vendas do Ciclo 202410',\n",
+ " 'Histórico de Vendas do Ciclo 202411',\n",
+ " 'Histórico de Vendas do Ciclo 202412',\n",
+ " 'Histórico de Vendas do Ciclo 202413',\n",
+ " 'Histórico de Vendas do Ciclo 202414',\n",
+ " 'Histórico de Vendas do Ciclo 202415',\n",
+ " 'Histórico de Vendas do Ciclo 202416',\n",
+ " 'Histórico de Vendas do Ciclo 202417',\n",
+ " 'Histórico de Vendas do Ciclo 202501',\n",
+ " 'Histórico de Vendas do Ciclo 202502',\n",
+ " 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo Atual'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_draft.columns[7:25]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "34e179cb",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\joao.herculano\\AppData\\Local\\Temp\\ipykernel_61796\\1463083786.py:24: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n",
+ " crescimento_por_pdv = df_draft.groupby('PDV').apply(calcular_crescimento)\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Define as colunas mensais\n",
+ "colunas_mensais = df_draft.columns[7:25]\n",
+ "\n",
+ "# Agrupa por PDV e calcula crescimento médio por PDV\n",
+ "def calcular_crescimento(grupo):\n",
+ " soma_mensal = grupo[colunas_mensais].sum() # soma por mês\n",
+ " variacao_mensal = soma_mensal.pct_change().dropna() # variação percentual mês a mês\n",
+ " variacao_mensal = variacao_mensal[np.isfinite(variacao_mensal)]\n",
+ "\n",
+ " if len(variacao_mensal) == 0:\n",
+ " return pd.Series({'CRESCIMENTO': np.nan})\n",
+ "\n",
+ " media = variacao_mensal.mean()\n",
+ " desvio = variacao_mensal.std()\n",
+ "\n",
+ " limite_sup = media + 2 * desvio\n",
+ " limite_inf = media - 2 * desvio\n",
+ "\n",
+ " variacoes_filtradas = variacao_mensal[variacao_mensal.between(limite_inf, limite_sup)]\n",
+ " crescimento = round(variacoes_filtradas.mean(), 4)\n",
+ " return pd.Series({'CRESCIMENTO': crescimento})\n",
+ "\n",
+ "# Aplica a função por PDV\n",
+ "crescimento_por_pdv = df_draft.groupby('PDV').apply(calcular_crescimento)\n",
+ "\n",
+ "# Merge do resultado de volta no dataframe original\n",
+ "df_draft = df_draft.merge(crescimento_por_pdv, on='PDV', how='left')\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "4bc8c2b4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares['PDV'] = df_similares['PDV'].astype('Int64')\n",
+ "\n",
+ "df_final = pd.merge(left=df_similares,right=df_draft,right_on=['PDV', 'SKU'],left_on=['PDV','PRODUTO SIMILAR'],how='left')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "c1451562",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(2455, 14)"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_venda_diaria = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\EUDORA\\C11\\arquivos usados na sugestão\\VENDAS_DIARIAS\\FormFiltroConsultaVendaSintetica_23_05_2025_09_22_15.xls\")\n",
+ "\n",
+ "df_venda_diaria.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "882e68aa",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['Unidade de Negócio', 'Ano', 'Mês', 'Dia', 'Código do Produto',\n",
+ " 'Descrição do Produto', 'Quantidade', 'Valor Bruto', 'Valor Desconto',\n",
+ " 'Valor Líquido', 'Valor Vale Troca', 'Líquido - Troca', 'Estoque Atual',\n",
+ " 'Estoque Mínimo'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_venda_diaria.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "c7ddaf20",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(2455, 16)"
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_venda_diaria['PDV'] = df_venda_diaria['Unidade de Negócio'].str.split(\"-\").str[0].str.strip()\n",
+ "\n",
+ "df_venda_diaria['Dia'] = pd.to_datetime(df_venda_diaria['Dia'], format='%d/%m/%Y')\n",
+ "\n",
+ "df_venda_diaria = pd.merge(left=df_venda_diaria,right=calendario[['Ciclo','Date']],left_on='Dia',right_on='Date',how='inner')\n",
+ "\n",
+ "df_venda_diaria = df_venda_diaria.drop(columns='Date')\n",
+ "\n",
+ "df_venda_diaria.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "id": "7119556a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(2455, 17)"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# 'Dia' já está em formato datetime, então renomeamos para 'Data' diretamente\n",
+ "# ou apenas usamos 'Dia' como referência de data\n",
+ "\n",
+ "# Ordena o DataFrame para garantir que a cumulativa funcione corretamente\n",
+ "df_venda_diaria = df_venda_diaria.sort_values(by=['Unidade de Negócio', 'Código do Produto', 'Dia'])\n",
+ "\n",
+ "# Calcula a quantidade acumulada até o dia para cada grupo\n",
+ "df_venda_diaria['Quantidade Acumulada'] = (\n",
+ " df_venda_diaria\n",
+ " .groupby(['Unidade de Negócio', 'Código do Produto'])['Quantidade']\n",
+ " .cumsum()\n",
+ ") # acumulado por grupo até a data da linha\n",
+ "\n",
+ "df_venda_diaria.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "c707a1b6",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " PDV | \n",
+ " Código do Produto | \n",
+ " Ciclo | \n",
+ " Quantidade Acumulada | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 20968 | \n",
+ " 50112 | \n",
+ " C202310 | \n",
+ " 44 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 20968 | \n",
+ " 50112 | \n",
+ " C202311 | \n",
+ " 60 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 20968 | \n",
+ " 50112 | \n",
+ " C202313 | \n",
+ " 70 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 20968 | \n",
+ " 50112 | \n",
+ " C202314 | \n",
+ " 73 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 20968 | \n",
+ " 50112 | \n",
+ " C202315 | \n",
+ " 75 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 528 | \n",
+ " 22541 | \n",
+ " 52699 | \n",
+ " C202409 | \n",
+ " 10 | \n",
+ "
\n",
+ " \n",
+ " | 529 | \n",
+ " 22541 | \n",
+ " 52699 | \n",
+ " C202410 | \n",
+ " 11 | \n",
+ "
\n",
+ " \n",
+ " | 530 | \n",
+ " 22541 | \n",
+ " 52699 | \n",
+ " C202411 | \n",
+ " 13 | \n",
+ "
\n",
+ " \n",
+ " | 531 | \n",
+ " 22541 | \n",
+ " 56572 | \n",
+ " C202411 | \n",
+ " 38 | \n",
+ "
\n",
+ " \n",
+ " | 532 | \n",
+ " 22541 | \n",
+ " 57390 | \n",
+ " C202411 | \n",
+ " 22 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
533 rows × 4 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " PDV Código do Produto Ciclo Quantidade Acumulada\n",
+ "0 20968 50112 C202310 44\n",
+ "1 20968 50112 C202311 60\n",
+ "2 20968 50112 C202313 70\n",
+ "3 20968 50112 C202314 73\n",
+ "4 20968 50112 C202315 75\n",
+ ".. ... ... ... ...\n",
+ "528 22541 52699 C202409 10\n",
+ "529 22541 52699 C202410 11\n",
+ "530 22541 52699 C202411 13\n",
+ "531 22541 56572 C202411 38\n",
+ "532 22541 57390 C202411 22\n",
+ "\n",
+ "[533 rows x 4 columns]"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_venda_diaria = df_venda_diaria.drop_duplicates()\n",
+ "\n",
+ "df_venda_agrupado = df_venda_diaria.groupby(['PDV', 'Código do Produto','Ciclo'])['Quantidade Acumulada'].max().reset_index()\n",
+ "df_venda_agrupado"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "id": "dc452c72",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(170, 75)"
+ ]
+ },
+ "execution_count": 20,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=filtered_calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO','match','dias_ate_inicio']], right_on='match',left_on='MATCH',how='left')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "id": "c260e0e3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#df_final = df_final.drop(columns=['PDV DESC','status','SKU','Descrição','Lançamento','Item analisado','Planograma','Quantidade por caixa'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "8a05450c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO']], right_on='Ciclo',left_on='CICLO SIMILAR',how='left')\n",
+ "df_final.shape\n",
+ "\n",
+ "df_final = df_final.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "cc65edab",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "df_venda_agrupado = df_venda_agrupado.rename(columns={'Quantidade Acumulada':'Vendas Ciclo Lançamento'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "c5cd5f42",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PRODUTO LANÇAMENTO'] = df_final['PRODUTO LANÇAMENTO'].astype('Int64')\n",
+ "\n",
+ "df_venda_agrupado['PDV'] = df_venda_agrupado['PDV'].astype('Int64')\n",
+ "\n",
+ "df_final = pd.merge(left=df_final, right = df_venda_agrupado, right_on=['Ciclo','Código do Produto','PDV'],left_on=['CICLO SIMILAR','PRODUTO SIMILAR','PDV'],how='left')\n",
+ "\n",
+ "df_final = df_final.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "id": "69c88d20",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "np.int64(17)"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['PDV'].value_counts().min()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "id": "f5206f50",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['Histórico de Vendas do Ciclo 202407',\n",
+ " 'Histórico de Vendas do Ciclo 202408',\n",
+ " 'Histórico de Vendas do Ciclo 202409',\n",
+ " 'Histórico de Vendas do Ciclo 202410',\n",
+ " 'Histórico de Vendas do Ciclo 202411',\n",
+ " 'Histórico de Vendas do Ciclo 202412',\n",
+ " 'Histórico de Vendas do Ciclo 202413',\n",
+ " 'Histórico de Vendas do Ciclo 202414',\n",
+ " 'Histórico de Vendas do Ciclo 202415',\n",
+ " 'Histórico de Vendas do Ciclo 202416',\n",
+ " 'Histórico de Vendas do Ciclo 202417',\n",
+ " 'Histórico de Vendas do Ciclo 202501',\n",
+ " 'Histórico de Vendas do Ciclo 202502',\n",
+ " 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 26,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns[29:46]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "id": "0a1bb832",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "np.float64(0.2176)"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Suponha que os meses estão nas colunas 10 a 26 (17 colunas = 17 meses)\n",
+ "colunas_mensais = df_final.columns[29:46]\n",
+ "\n",
+ "# Passo 1: Soma todas as linhas (itens) por mês → resultado: total por mês\n",
+ "soma_mensal = df_final[colunas_mensais].sum()\n",
+ "\n",
+ "# Passo 2: Calcula a variação percentual de um mês para o outro\n",
+ "variacao_mensal = soma_mensal.pct_change()\n",
+ "variacao_mensal = variacao_mensal.dropna()\n",
+ "\n",
+ "variacao_mensal = variacao_mensal[np.isfinite(variacao_mensal)]\n",
+ "\n",
+ "# Passo 3: Calcula a média da variação (ignorando o primeiro NaN)\n",
+ "media_variacao = variacao_mensal[1:].mean()\n",
+ "\n",
+ "# Calcula média e desvio padrão\n",
+ "media = variacao_mensal.mean()\n",
+ "desvio = variacao_mensal.std()\n",
+ "\n",
+ "# Define limite (ex: 2 desvios padrão)\n",
+ "limite_superior = media + 2 * desvio\n",
+ "limite_inferior = media - 2 * desvio\n",
+ "\n",
+ "# Filtra dados dentro do limite\n",
+ "filtro = variacao_mensal.between(limite_inferior, limite_superior)\n",
+ "df_filtrado = variacao_mensal[filtro]\n",
+ "CRESCIMENTO = round(df_filtrado.mean(),4)\n",
+ "\n",
+ "df_final['CRESCIMENTO_GERAL'] = CRESCIMENTO\n",
+ "\n",
+ "CRESCIMENTO\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "id": "a9647c32",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns='Ciclo_y')\n",
+ "\n",
+ "df_final = df_final.rename(columns={'Ciclo_x': 'Ciclo',\t'INICIO CICLO_x': 'INICIO CICLO',\t'FIM CICLO_x':'FIM CICLO' ,'DURAÇÃO_x':'DURAÇÃO',\n",
+ " \t'INICIO CICLO_y': 'INICIO CICLO SIMILAR' ,\t'FIM CICLO_y': 'FIM CICLO SIMILAR','DURAÇÃO_y':'DURAÇÃO CICLO SIMILAR'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "id": "b107e519",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['Histórico de Vendas do Ciclo 202501',\n",
+ " 'Histórico de Vendas do Ciclo 202502',\n",
+ " 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo Atual'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 29,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns[40:47]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "id": "8290853c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "VENDA_SIMILAR_6_MESES= df_final.columns[40:47]\n",
+ "\n",
+ "df_final['Pico Vendas Similar Ultimos 6 ciclos'] = df_final[VENDA_SIMILAR_6_MESES].max(axis=1)\n",
+ "\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = df_final[colunas_mensais].dropna().median(axis=1)\n",
+ "\n",
+ "df_final['Vendas Ciclo Lançamento'] = df_final['Vendas Ciclo Lançamento'].fillna(0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "id": "07f043f2",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " CANAL | \n",
+ " med_por_canal | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " CD | \n",
+ " 17.0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " HIB | \n",
+ " 1.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " VD | \n",
+ " 12.0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " CANAL med_por_canal\n",
+ "0 CD 17.0\n",
+ "1 HIB 1.0\n",
+ "2 VD 12.0"
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "medi = df_final.groupby(['CANAL'])['MEDIANA DO HISTÓRICO'].max().reset_index()\n",
+ "medi = medi.rename(columns={'MEDIANA DO HISTÓRICO':'med_por_canal'})\n",
+ "medi"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "id": "94abddce",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=medi,on='CANAL',how='inner')\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "id": "09cc2f82",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_vdc = pd.read_csv(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BOT\\BOT - C11\\arquivos para geração da sugestão\\VENDAS VDC\\vendas_vdc22.02.csv\")\n",
+ "\n",
+ "\n",
+ "\n",
+ "df_vdc['DATA VENDA'] = pd.to_datetime(df_vdc['DATA VENDA'])\n",
+ "\n",
+ "# 'Dia' já está em formato datetime, então renomeamos para 'Data' diretamente\n",
+ "# ou apenas usamos 'Dia' como referência de data\n",
+ "\n",
+ "# Ordena o DataFrame para garantir que a cumulativa funcione corretamente\n",
+ "df_venda_diaria = df_venda_diaria.sort_values(by=['Unidade de Negócio', 'Código do Produto', 'Dia'])\n",
+ "\n",
+ "# Calcula a quantidade acumulada até o dia para cada grupo\n",
+ "df_vdc['Quantidade Acumulada vdc'] = (\n",
+ " df_vdc\n",
+ " .groupby(['PDVDEPARA.Practico', 'Código'])['Soma de Quantidade']\n",
+ " .cumsum()\n",
+ ") # acumulado por grupo até a data da linha"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "id": "5a827c08",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " PDVDEPARA.Practico | \n",
+ " Código | \n",
+ " Ciclo vdc | \n",
+ " Quantidade Acumulada vdc | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 23701 | \n",
+ " 48617 | \n",
+ " C202301 | \n",
+ " 24 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 23701 | \n",
+ " 50165 | \n",
+ " C202312 | \n",
+ " 12 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 23701 | \n",
+ " 50165 | \n",
+ " C202411 | \n",
+ " 14 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 23701 | \n",
+ " 50165 | \n",
+ " C202413 | \n",
+ " 16 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 23701 | \n",
+ " 50224 | \n",
+ " C202403 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " PDVDEPARA.Practico Código Ciclo vdc Quantidade Acumulada vdc\n",
+ "0 23701 48617 C202301 24\n",
+ "1 23701 50165 C202312 12\n",
+ "2 23701 50165 C202411 14\n",
+ "3 23701 50165 C202413 16\n",
+ "4 23701 50224 C202403 2"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_vdc = pd.merge(left=df_vdc,right=calendario[['Date','Ciclo']],left_on='DATA VENDA',right_on='Date',how='inner')\n",
+ "\n",
+ "df_vdc_agrupado = df_vdc.groupby(['PDVDEPARA.Practico',\t'Código','Ciclo'])['Quantidade Acumulada vdc'].max().reset_index()\n",
+ "\n",
+ "df_vdc_agrupado = df_vdc_agrupado.rename(columns={'Ciclo':'Ciclo vdc'})\n",
+ "\n",
+ "\n",
+ "df_vdc_agrupado.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "id": "8ec14143",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right = df_vdc_agrupado, right_on=['Ciclo vdc','Código','PDVDEPARA.Practico'],left_on=['CICLO SIMILAR','PRODUTO SIMILAR','PDV'],how='left')\n",
+ "\n",
+ "df_final['Quantidade Acumulada vdc'] = df_final['Quantidade Acumulada vdc'].fillna(0)\n",
+ "\n",
+ "\n",
+ "df_final['Vendas Ciclo Lançamento'] = np.where(df_final['Quantidade Acumulada vdc']>0, df_final['Quantidade Acumulada vdc'], df_final['Vendas Ciclo Lançamento'])\n",
+ "\n",
+ "df_final = df_final.drop(columns='Quantidade Acumulada vdc')\n",
+ "\n",
+ "\n",
+ "df_final = df_final.drop(columns='Ciclo vdc')\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "id": "27906593",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['CRESCIMENTO_FINAL'] = df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'] #crescimento do pdv\n",
+ "\n",
+ "df_final['CRESCIMENTO_FINAL'] = np.where(df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO']>0.8,0.8,df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'])\n",
+ "\n",
+ "df_final['CRESCIMENTO_FINAL'] = np.where(df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO']<0,0,df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'])\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = np.where(df_final['MEDIANA DO HISTÓRICO']==0, df_final['med_por_canal'],df_final['MEDIANA DO HISTÓRICO'])\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "id": "5ba0586e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "np.int64(0)"
+ ]
+ },
+ "execution_count": 37,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['med_por_canal'].isna().sum()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "id": "1a625e69",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(170, 89)"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\n",
+ "# Primeiro cálculo intermediário\n",
+ "df_final['PV GINSENG'] = np.where(df_final['CRESCIMENTO_FINAL'] * df_final['Vendas Ciclo Lançamento'] + df_final['Vendas Ciclo Lançamento'] < df_final['MEDIANA DO HISTÓRICO'],\n",
+ " round(df_final['CRESCIMENTO_FINAL'] * df_final['MEDIANA DO HISTÓRICO']+ df_final['MEDIANA DO HISTÓRICO'],0), \n",
+ " round(df_final['CRESCIMENTO_FINAL']*df_final['Vendas Ciclo Lançamento']+df_final['Vendas Ciclo Lançamento'],0))\n",
+ "\n",
+ "df_final['PV GINSENG'] = np.where(df_final['PV GINSENG'].isna(),df_final['med_por_canal'] ,df_final['PV GINSENG'])\n",
+ "\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "id": "ad10c069",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.drop(columns=df_final.columns[29:42],inplace=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "id": "f9bddbb1",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PRODUTO LANÇAMENTO', 'DESCRIÇÃO DO LANÇAMENTO', 'PRODUTO SIMILAR',\n",
+ " 'DESCRIÇÃO SIMILAR', 'CICLO SIMILAR', 'FOCO', 'IAF', 'CATEGORIA',\n",
+ " 'MARCA', '% CONSUMIDOR', 'MECANICA CONSUMIDOR', '% REVENDEDOR',\n",
+ " 'MECANICA REVENDEDOR', 'TIPO DE PRODUTO', 'MATCH', 'PDV', 'CANAL',\n",
+ " 'DESCRIÇÃO PDV', 'PDV DESC', 'UF', 'ANALISTA', 'SUPERVISOR', 'status',\n",
+ " 'Classe', 'SKU', 'Descrição', 'Subcategoria', 'Lançamento',\n",
+ " 'Desativação', 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'Dias sem venda',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1',\n",
+ " 'Promoção Próximo Ciclo', 'Promoção Próximo Ciclo + 1', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Pedido Pendente',\n",
+ " 'Compra inteligente semanal/Sugestão de compra',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'Carteira Bloqueada Para Novos Pedidos',\n",
+ " 'Planograma', 'Quantidade por caixa', 'Preço Sell In', 'Quantidade',\n",
+ " 'Item analisado', 'Histórico de Vendas do Ciclo 202507', 'match_x',\n",
+ " 'CRESCIMENTO', 'Ciclo', 'INICIO CICLO', 'FIM CICLO', 'DURAÇÃO',\n",
+ " 'match_y', 'dias_ate_inicio', 'INICIO CICLO SIMILAR',\n",
+ " 'FIM CICLO SIMILAR', 'DURAÇÃO CICLO SIMILAR', 'Código do Produto',\n",
+ " 'Ciclo', 'Vendas Ciclo Lançamento', 'CRESCIMENTO_GERAL',\n",
+ " 'Pico Vendas Similar Ultimos 6 ciclos', 'MEDIANA DO HISTÓRICO',\n",
+ " 'med_por_canal', 'PDVDEPARA.Practico', 'Código', 'CRESCIMENTO_FINAL',\n",
+ " 'PV GINSENG'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "id": "fe73c93e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PRODUTO LANÇAMENTO', 'DESCRIÇÃO DO LANÇAMENTO', 'PRODUTO SIMILAR',\n",
+ " 'DESCRIÇÃO SIMILAR', 'CICLO SIMILAR', 'FOCO', 'IAF', 'CATEGORIA',\n",
+ " 'MARCA', '% CONSUMIDOR', 'MECANICA CONSUMIDOR', '% REVENDEDOR',\n",
+ " 'MECANICA REVENDEDOR', 'TIPO DE PRODUTO', 'MATCH', 'PDV', 'CANAL',\n",
+ " 'DESCRIÇÃO PDV', 'PDV DESC', 'UF', 'ANALISTA', 'SUPERVISOR',\n",
+ " 'Descrição', 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo Atual',\n",
+ " 'Histórico de Vendas do Ciclo 202507', 'Vendas Ciclo Lançamento',\n",
+ " 'Pico Vendas Similar Ultimos 6 ciclos', 'MEDIANA DO HISTÓRICO',\n",
+ " 'PDVDEPARA.Practico', 'Código', 'PV GINSENG'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 41,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\n",
+ "df_final.drop(columns=['status', 'Classe', 'SKU', 'Subcategoria', 'Lançamento',\n",
+ " 'Desativação','Dias sem venda',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1',\n",
+ " 'Promoção Próximo Ciclo', 'Promoção Próximo Ciclo + 1', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Pedido Pendente',\n",
+ " 'Compra inteligente semanal/Sugestão de compra',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'Carteira Bloqueada Para Novos Pedidos',\n",
+ " 'Planograma', 'Quantidade por caixa', 'Preço Sell In', 'Quantidade',\n",
+ " 'Item analisado', 'match_x',\n",
+ " 'CRESCIMENTO', 'Ciclo', 'INICIO CICLO', 'FIM CICLO', 'DURAÇÃO',\n",
+ " 'match_y', 'dias_ate_inicio', 'INICIO CICLO SIMILAR','med_por_canal', 'CRESCIMENTO_FINAL',\n",
+ " 'FIM CICLO SIMILAR', 'DURAÇÃO CICLO SIMILAR', 'Código do Produto','CRESCIMENTO_GERAL'],inplace=True)\n",
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "66772a9a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo Atual'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 42,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns[23:28]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "15b7149f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.rename(columns={df_final.columns[23]: \"C-4\", df_final.columns[24]: \"C-3\",df_final.columns[25]: \"C-2\",df_final.columns[26]: \"C-1\",df_final.columns[27]:'VENDAS CICLO ATUAL'})\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "id": "9333bc77",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PRODUTO LANÇAMENTO', 'DESCRIÇÃO DO LANÇAMENTO', 'PRODUTO SIMILAR',\n",
+ " 'DESCRIÇÃO SIMILAR', 'CICLO SIMILAR', 'FOCO', 'IAF', 'CATEGORIA',\n",
+ " 'MARCA', '% CONSUMIDOR', 'MECANICA CONSUMIDOR', '% REVENDEDOR',\n",
+ " 'MECANICA REVENDEDOR', 'TIPO DE PRODUTO', 'MATCH', 'PDV', 'CANAL',\n",
+ " 'DESCRIÇÃO PDV', 'PDV DESC', 'UF', 'ANALISTA', 'SUPERVISOR',\n",
+ " 'DESCRIÇÃO', 'C-4', 'C-3', 'C-2', 'C-1', 'VENDAS CICLO ATUAL',\n",
+ " 'VENDAS CICLO LANÇAMENTO', 'PICO VENDAS SIMILAR ULTIMOS 6 CICLOS',\n",
+ " 'MEDIANA DO HISTÓRICO', 'PDVDEPARA.PRACTICO', 'CÓDIGO', 'PV GINSENG'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns = df_final.columns.str.upper()\n",
+ "\n",
+ "df_final.drop(columns=df_final.filter(regex='HISTÓRICO DE VENDAS DO CICLO').columns, inplace=True)\n",
+ "\n",
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5abd4bae",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns=['DESCRIÇÃO','MEDIANA DO HISTÓRICO','SKU'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "id": "62ce5c62",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(170, 32)"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "id": "25cbff26",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.reindex(columns=[\n",
+ " 'SUPERVISOR',\n",
+ " 'ANALISTA',\n",
+ " 'CANAL',\n",
+ " 'UF',\n",
+ " 'PDV',\n",
+ " 'PDV DESC',\n",
+ " 'PRODUTO LANÇAMENTO',\n",
+ " 'DESCRIÇÃO DO LANÇAMENTO',\n",
+ " 'MARCA',\n",
+ " 'CATEGORIA',\n",
+ " 'MECANICA CONSUMIDOR',\n",
+ " '% CONSUMIDOR',\n",
+ " 'MECANICA REVENDEDOR',\n",
+ " '% REVENDEDOR',\n",
+ " 'TIPO DE PRODUTO',\n",
+ " 'IAF',\n",
+ " 'FOCO',\n",
+ " 'SKU',\n",
+ " 'PRODUTO SIMILAR',\n",
+ " 'DESCRIÇÃO SIMILAR',\n",
+ " 'CICLO SIMILAR',\n",
+ " 'VENDAS CICLO LANÇAMENTO',\n",
+ " 'C-4',\n",
+ " 'C-3',\n",
+ " 'C-2',\n",
+ " 'C-1',\n",
+ " 'VENDAS CICLO ATUAL',\n",
+ " 'PICO VENDAS SIMILAR ULTIMOS 6 CICLOS',\n",
+ " 'PV GINSENG'])\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "id": "a3e80cb4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['SUGESTÃO METASELLIN'] = ''\n",
+ "df_final['SUGESTÃO ABASTECIMENTO'] = ''\n",
+ "df_final['SUGESTÃO COMERCIAL'] = ''\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "id": "2df3e2e9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.to_excel(r'C:\\Users\\joao.herculano\\Documents\\sugestEUD.xlsx',index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/Lançamentos/Script_lançamento_boti_v2.ipynb b/Lançamentos/Script_lançamento_boti_v2.ipynb
new file mode 100644
index 0000000..6aa17d9
--- /dev/null
+++ b/Lançamentos/Script_lançamento_boti_v2.ipynb
@@ -0,0 +1,785 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6ad35669",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "import numpy as np \n",
+ "import glob\n",
+ "import os "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9fcdc77a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "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\")\n",
+ "\n",
+ "calendario['Date'] = pd.to_datetime(calendario['Date'])\n",
+ "\n",
+ "# Get today (normalized to midnight)\n",
+ "today = pd.Timestamp(\"today\").normalize()\n",
+ "\n",
+ "calendario['NUM_CICLO'] = calendario['Ciclo'].str[-2:].astype(int)\n",
+ "\n",
+ "calendario['ANO_CICLO'] = calendario['Ciclo'].str[0:5]\n",
+ "\n",
+ "calendario = calendario[calendario['MARCA'] == \"BOTICARIO\"]\n",
+ "\n",
+ "calendario['CICLOMAIS2'] = calendario['ANO_CICLO'].astype(str) + (calendario['NUM_CICLO'].astype(int) + 3).astype(str).str.zfill(2) #<<< MUDAR O \"4\" (CICLO ATUAL + 4 PARA ACHAR O CICLO DA SUGESTÃO) EX: C202505 -> C202509\n",
+ "ciclo_mais2 = calendario[calendario['Date'].dt.normalize() == today]['CICLOMAIS2'].iloc[0]\n",
+ "\n",
+ "# Filter rows where date matches today\n",
+ "filtered_calendario = calendario[calendario['Ciclo'] == ciclo_mais2][:1]\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['INICIO CICLO'].iloc[0] - today\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['dias_ate_inicio'].dt.days.astype(int)\n",
+ "\n",
+ "filtered_calendario['match'] = 1\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bbec229d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "filtered_calendario"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "61ffc777",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BOT\\BOT - C11\\arquivos para geração da sugestão\\SIMILARES\\PRODUTOS SIMILARES - BOT.xlsx\")\n",
+ "\n",
+ "df_similares = pd.merge(left=df_similares,right=calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO']], how= 'left', left_on = 'CICLO SIMILAR',right_on = 'Ciclo' )\n",
+ "\n",
+ "df_similares = df_similares.drop(columns=['Ciclo'])\n",
+ "\n",
+ "df_similares = df_similares.rename(columns={'INICIO CICLO':'INICIO CICLO SIMILAR','FIM CICLO':'FIM CICLO SIMILAR','DURAÇÃO':'DURAÇÃO CICLO SIMILAR'})\n",
+ "df_similares.drop_duplicates(inplace=True)\n",
+ "\n",
+ "df_similares['MATCH'] = 1\n",
+ "\n",
+ "df_similares = df_similares.drop(columns=['INICIO DO CICLO',\n",
+ " 'FIM DO CICLO', 'DURAÇÃO CICLO','INICIO CICLO SIMILAR','FIM CICLO SIMILAR','DURAÇÃO CICLO SIMILAR'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "99ea95e6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fe922f62",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BOT\\BOT - C11\\arquivos para geração da sugestão\\TABELA DE PEDIDO\\Pedidos Semanais Especiais - BOT - 202511.xlsx\")\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Região'] == 'NNE'] \n",
+ "\n",
+ "df_tabela = df_tabela[(df_tabela['Canal'] != 'Ecomm') | (df_tabela['Canal'] != 'Ecomm | VD') | (df_tabela['Canal'] != 'Ecomm | Loja')] \n",
+ "\n",
+ "df_tabela['Canal'] = np.where((df_tabela['Canal'] == \"Loja\") | (df_tabela['Canal'] == \"Todos\") | (df_tabela['Canal'] == \"Loja | VD\"),\"TODOS\",\"VD\")\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Tipo de promoção'].str.contains('Lançamentos', na=False)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a3a045d9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BASE DE DADOS LANÇAMENTO\\BOT\\PDV\\PDV_ATT.xlsx\")\n",
+ "\n",
+ "df_pdv_origi = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BASE DE DADOS LANÇAMENTO\\BOT\\PDV\\PDV_ATT.xlsx\")\n",
+ "\n",
+ "df_pdv = df_pdv.rename(columns={'DESCRIÇÃO':'DESCRIÇÃO PDV'})\n",
+ "\n",
+ "df_pdv = df_pdv.drop(columns=['REGIÃO', 'ESTADO','CIDADE','GESTÃO','MARCA'])\n",
+ "\n",
+ "df_pdv['PDV'] = df_pdv['PDV DESC'].str.split(\"-\").str[0].str.strip()\n",
+ "\n",
+ "df_pdv['UF'] = np.where(df_pdv['UF'] == 'VDC','BA',df_pdv['UF'])\n",
+ "\n",
+ "#ignorando a PDV que ainda não está online\n",
+ "df_pdv = df_pdv[df_pdv['DESCRIÇÃO PDV'] != '23813-COMERCIO-HIB VALENTE']\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['status'] != \"INATIVO\"]\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['status'] != \"MATRIZ\"]\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['SUPERVISOR'] != 'Inativa']\n",
+ "\n",
+ "df_pdv['MATCH'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "849d5297",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "df04a501",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares = pd.merge(left=df_similares,right=df_pdv,right_on=['MATCH'],left_on=['MATCH'],how='inner')\n",
+ "\n",
+ "df_similares = df_similares.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0da911af",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Caminho onde estão as subpastas com os arquivos CSV\n",
+ "\n",
+ "# Set the path to the folder containing CSV files\n",
+ "folder_path = r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BOT\\BOT - C11\\arquivos para geração da sugestão\\DRAFT\" # arquivo dos drafts\n",
+ "\n",
+ "# Pattern to match all CSV files\n",
+ "csv_files = glob.glob(os.path.join(folder_path, '*.csv'))\n",
+ "\n",
+ "# Read and concat all CSVs\n",
+ "df_draft = pd.concat([pd.read_csv(file) for file in csv_files], ignore_index=True)\n",
+ "\n",
+ "df_draft['match'] = 1 \n",
+ "\n",
+ "df_draft.shape\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0c8c7493",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft = df_draft.drop(columns=['Categoria'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "91298cde",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft.columns[7:25]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "34e179cb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define as colunas mensais\n",
+ "colunas_mensais = df_draft.columns[7:25]\n",
+ "\n",
+ "# Agrupa por PDV e calcula crescimento médio por PDV\n",
+ "def calcular_crescimento(grupo):\n",
+ " soma_mensal = grupo[colunas_mensais].sum() # soma por mês\n",
+ " variacao_mensal = soma_mensal.pct_change().dropna() # variação percentual mês a mês\n",
+ " variacao_mensal = variacao_mensal[np.isfinite(variacao_mensal)]\n",
+ "\n",
+ " if len(variacao_mensal) == 0:\n",
+ " return pd.Series({'CRESCIMENTO': np.nan})\n",
+ "\n",
+ " media = variacao_mensal.mean()\n",
+ " desvio = variacao_mensal.std()\n",
+ "\n",
+ " limite_sup = media + 2 * desvio\n",
+ " limite_inf = media - 2 * desvio\n",
+ "\n",
+ " variacoes_filtradas = variacao_mensal[variacao_mensal.between(limite_inf, limite_sup)]\n",
+ " crescimento = round(variacoes_filtradas.mean(), 4)\n",
+ " return pd.Series({'CRESCIMENTO': crescimento})\n",
+ "\n",
+ "# Aplica a função por PDV\n",
+ "crescimento_por_pdv = df_draft.groupby('PDV').apply(calcular_crescimento)\n",
+ "\n",
+ "# Merge do resultado de volta no dataframe original\n",
+ "df_draft = df_draft.merge(crescimento_por_pdv, on='PDV', how='left')\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4bc8c2b4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_similares['PDV'] = df_similares['PDV'].astype('Int64')\n",
+ "\n",
+ "df_final = pd.merge(left=df_similares,right=df_draft,right_on=['PDV', 'SKU'],left_on=['PDV','PRODUTO SIMILAR'],how='left')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c1451562",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_venda_diaria = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BOT\\BOT - C11\\arquivos para geração da sugestão\\VENDAS_DIARIAS\\FormFiltroConsultaVendaSintetica_22_05_2025_16_26_17.xls\")\n",
+ "\n",
+ "df_venda_diaria.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "882e68aa",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_venda_diaria.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c7ddaf20",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_venda_diaria['PDV'] = df_venda_diaria['Unidade de Negócio'].str.split(\"-\").str[0].str.strip()\n",
+ "\n",
+ "df_venda_diaria['Dia'] = pd.to_datetime(df_venda_diaria['Dia'], format='%d/%m/%Y')\n",
+ "\n",
+ "df_venda_diaria = pd.merge(left=df_venda_diaria,right=calendario[['Ciclo','Date']],left_on='Dia',right_on='Date',how='inner')\n",
+ "\n",
+ "df_venda_diaria = df_venda_diaria.drop(columns='Date')\n",
+ "\n",
+ "df_venda_diaria.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7119556a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# 'Dia' já está em formato datetime, então renomeamos para 'Data' diretamente\n",
+ "# ou apenas usamos 'Dia' como referência de data\n",
+ "\n",
+ "# Ordena o DataFrame para garantir que a cumulativa funcione corretamente\n",
+ "df_venda_diaria = df_venda_diaria.sort_values(by=['Unidade de Negócio', 'Código do Produto', 'Dia'])\n",
+ "\n",
+ "# Calcula a quantidade acumulada até o dia para cada grupo\n",
+ "df_venda_diaria['Quantidade Acumulada'] = (\n",
+ " df_venda_diaria\n",
+ " .groupby(['Unidade de Negócio', 'Código do Produto'])['Quantidade']\n",
+ " .cumsum()\n",
+ ") # acumulado por grupo até a data da linha\n",
+ "\n",
+ "df_venda_diaria.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c707a1b6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_venda_diaria = df_venda_diaria.drop_duplicates()\n",
+ "\n",
+ "df_venda_agrupado = df_venda_diaria.groupby(['PDV', 'Código do Produto','Ciclo'])['Quantidade Acumulada'].max().reset_index()\n",
+ "df_venda_agrupado"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "dc452c72",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=filtered_calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO','match','dias_ate_inicio']], right_on='match',left_on='MATCH',how='left')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c260e0e3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#df_final = df_final.drop(columns=['PDV DESC','status','SKU','Descrição','Lançamento','Item analisado','Planograma','Quantidade por caixa'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8a05450c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO']], right_on='Ciclo',left_on='CICLO SIMILAR',how='left')\n",
+ "df_final.shape\n",
+ "\n",
+ "df_final = df_final.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cc65edab",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "df_venda_agrupado = df_venda_agrupado.rename(columns={'Quantidade Acumulada':'Vendas Ciclo Lançamento'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c5cd5f42",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PRODUTO LANÇAMENTO'] = df_final['PRODUTO LANÇAMENTO'].astype('Int64')\n",
+ "\n",
+ "df_venda_agrupado['PDV'] = df_venda_agrupado['PDV'].astype('Int64')\n",
+ "\n",
+ "df_final = pd.merge(left=df_final, right = df_venda_agrupado, right_on=['Ciclo','Código do Produto','PDV'],left_on=['CICLO SIMILAR','PRODUTO SIMILAR','PDV'],how='left')\n",
+ "\n",
+ "df_final = df_final.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "69c88d20",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PDV'].value_counts().min()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f5206f50",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[29:46]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0a1bb832",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Suponha que os meses estão nas colunas 10 a 26 (17 colunas = 17 meses)\n",
+ "colunas_mensais = df_final.columns[29:46]\n",
+ "\n",
+ "# Passo 1: Soma todas as linhas (itens) por mês → resultado: total por mês\n",
+ "soma_mensal = df_final[colunas_mensais].sum()\n",
+ "\n",
+ "# Passo 2: Calcula a variação percentual de um mês para o outro\n",
+ "variacao_mensal = soma_mensal.pct_change()\n",
+ "variacao_mensal = variacao_mensal.dropna()\n",
+ "\n",
+ "variacao_mensal = variacao_mensal[np.isfinite(variacao_mensal)]\n",
+ "\n",
+ "# Passo 3: Calcula a média da variação (ignorando o primeiro NaN)\n",
+ "media_variacao = variacao_mensal[1:].mean()\n",
+ "\n",
+ "# Calcula média e desvio padrão\n",
+ "media = variacao_mensal.mean()\n",
+ "desvio = variacao_mensal.std()\n",
+ "\n",
+ "# Define limite (ex: 2 desvios padrão)\n",
+ "limite_superior = media + 2 * desvio\n",
+ "limite_inferior = media - 2 * desvio\n",
+ "\n",
+ "# Filtra dados dentro do limite\n",
+ "filtro = variacao_mensal.between(limite_inferior, limite_superior)\n",
+ "df_filtrado = variacao_mensal[filtro]\n",
+ "CRESCIMENTO = round(df_filtrado.mean(),4)\n",
+ "\n",
+ "df_final['CRESCIMENTO_GERAL'] = CRESCIMENTO\n",
+ "\n",
+ "CRESCIMENTO\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a9647c32",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns='Ciclo_y')\n",
+ "\n",
+ "df_final = df_final.rename(columns={'Ciclo_x': 'Ciclo',\t'INICIO CICLO_x': 'INICIO CICLO',\t'FIM CICLO_x':'FIM CICLO' ,'DURAÇÃO_x':'DURAÇÃO',\n",
+ " \t'INICIO CICLO_y': 'INICIO CICLO SIMILAR' ,\t'FIM CICLO_y': 'FIM CICLO SIMILAR','DURAÇÃO_y':'DURAÇÃO CICLO SIMILAR'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b107e519",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[40:47]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8290853c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "VENDA_SIMILAR_6_MESES= df_final.columns[40:47]\n",
+ "\n",
+ "df_final['Pico Vendas Similar Ultimos 6 ciclos'] = df_final[VENDA_SIMILAR_6_MESES].max(axis=1)\n",
+ "\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = df_final[colunas_mensais].dropna().median(axis=1)\n",
+ "\n",
+ "df_final['Vendas Ciclo Lançamento'] = df_final['Vendas Ciclo Lançamento'].fillna(0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "07f043f2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "medi = df_final.groupby(['CANAL'])['MEDIANA DO HISTÓRICO'].max().reset_index()\n",
+ "medi = medi.rename(columns={'MEDIANA DO HISTÓRICO':'med_por_canal'})\n",
+ "medi"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "94abddce",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=medi,on='CANAL',how='inner')\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "09cc2f82",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_vdc = pd.read_csv(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\BD_LANÇAMENTOS\\BOT\\BOT - C11\\arquivos para geração da sugestão\\VENDAS VDC\\vendas_vdc22.02.csv\")\n",
+ "\n",
+ "\n",
+ "\n",
+ "df_vdc['DATA VENDA'] = pd.to_datetime(df_vdc['DATA VENDA'])\n",
+ "\n",
+ "# 'Dia' já está em formato datetime, então renomeamos para 'Data' diretamente\n",
+ "# ou apenas usamos 'Dia' como referência de data\n",
+ "\n",
+ "# Ordena o DataFrame para garantir que a cumulativa funcione corretamente\n",
+ "df_venda_diaria = df_venda_diaria.sort_values(by=['Unidade de Negócio', 'Código do Produto', 'Dia'])\n",
+ "\n",
+ "# Calcula a quantidade acumulada até o dia para cada grupo\n",
+ "df_vdc['Quantidade Acumulada vdc'] = (\n",
+ " df_vdc\n",
+ " .groupby(['PDVDEPARA.Practico', 'Código'])['Soma de Quantidade']\n",
+ " .cumsum()\n",
+ ") # acumulado por grupo até a data da linha"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5a827c08",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_vdc = pd.merge(left=df_vdc,right=calendario[['Date','Ciclo']],left_on='DATA VENDA',right_on='Date',how='inner')\n",
+ "\n",
+ "df_vdc_agrupado = df_vdc.groupby(['PDVDEPARA.Practico',\t'Código','Ciclo'])['Quantidade Acumulada vdc'].max().reset_index()\n",
+ "\n",
+ "df_vdc_agrupado = df_vdc_agrupado.rename(columns={'Ciclo':'Ciclo vdc'})\n",
+ "\n",
+ "\n",
+ "df_vdc_agrupado.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8ec14143",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right = df_vdc_agrupado, right_on=['Ciclo vdc','Código','PDVDEPARA.Practico'],left_on=['CICLO SIMILAR','PRODUTO SIMILAR','PDV'],how='left')\n",
+ "\n",
+ "df_final['Quantidade Acumulada vdc'] = df_final['Quantidade Acumulada vdc'].fillna(0)\n",
+ "\n",
+ "\n",
+ "df_final['Vendas Ciclo Lançamento'] = np.where(df_final['Quantidade Acumulada vdc']>0, df_final['Quantidade Acumulada vdc'], df_final['Vendas Ciclo Lançamento'])\n",
+ "\n",
+ "df_final = df_final.drop(columns='Quantidade Acumulada vdc')\n",
+ "\n",
+ "\n",
+ "df_final = df_final.drop(columns='Ciclo vdc')\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1a625e69",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['CRESCIMENTO_FINAL'] = df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'] #crescimento do pdv\n",
+ "\n",
+ "df_final['CRESCIMENTO_FINAL'] = np.where(df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO']>0.8,0.8,df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'])\n",
+ "\n",
+ "df_final['CRESCIMENTO_FINAL'] = np.where(df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO']<0,0,df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'])\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = np.where(df_final['MEDIANA DO HISTÓRICO']==0, df_final['' \\\n",
+ "'or_canal'],df_final['MEDIANA DO HISTÓRICO'])\n",
+ "\n",
+ "# Primeiro cálculo intermediário\n",
+ "df_final['PV GINSENG'] = np.where(df_final['CRESCIMENTO_FINAL'] * df_final['Vendas Ciclo Lançamento'] + df_final['Vendas Ciclo Lançamento'] < df_final['MEDIANA DO HISTÓRICO'],\n",
+ " round(df_final['CRESCIMENTO_FINAL'] * df_final['MEDIANA DO HISTÓRICO']+ df_final['MEDIANA DO HISTÓRICO'],0), \n",
+ " round(df_final['CRESCIMENTO_FINAL']*df_final['Vendas Ciclo Lançamento']+df_final['Vendas Ciclo Lançamento'],0))\n",
+ "\n",
+ "df_final['PV GINSENG'] = np.where(df_final['PV GINSENG'].isna(),df_final['med_por_canal'] ,df_final['PV GINSENG'])\n",
+ "\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ad10c069",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.drop(columns=df_final.columns[29:42],inplace=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f9bddbb1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fe73c93e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "df_final.drop(columns=['status', 'Classe', 'SKU', 'Subcategoria', 'Lançamento',\n",
+ " 'Desativação','Dias sem venda',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1',\n",
+ " 'Promoção Próximo Ciclo', 'Promoção Próximo Ciclo + 1', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Pedido Pendente',\n",
+ " 'Compra inteligente semanal/Sugestão de compra',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'Carteira Bloqueada Para Novos Pedidos',\n",
+ " 'Planograma', 'Quantidade por caixa', 'Preço Sell In', 'Quantidade',\n",
+ " 'Item analisado', 'match_x',\n",
+ " 'CRESCIMENTO', 'Ciclo', 'INICIO CICLO', 'FIM CICLO', 'DURAÇÃO',\n",
+ " 'match_y', 'dias_ate_inicio', 'INICIO CICLO SIMILAR','med_por_canal', 'CRESCIMENTO_FINAL',\n",
+ " 'FIM CICLO SIMILAR', 'DURAÇÃO CICLO SIMILAR', 'Código do Produto','CRESCIMENTO_GERAL'],inplace=True)\n",
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "66772a9a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[23:28]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "15b7149f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.rename(columns={df_final.columns[23]: \"C-4\", df_final.columns[24]: \"C-3\",df_final.columns[25]: \"C-2\",df_final.columns[26]: \"C-1\",df_final.columns[27]:'VENDAS CICLO ATUAL'})\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9333bc77",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns = df_final.columns.str.upper()\n",
+ "\n",
+ "df_final.drop(columns=df_final.filter(regex='HISTÓRICO DE VENDAS DO CICLO').columns, inplace=True)\n",
+ "\n",
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5abd4bae",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns=['DESCRIÇÃO','MEDIANA DO HISTÓRICO'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "62ce5c62",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "25cbff26",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.reindex(columns=[\n",
+ " 'SUPERVISOR',\n",
+ " 'ANALISTA',\n",
+ " 'CANAL',\n",
+ " 'UF',\n",
+ " 'PDV',\n",
+ " 'PDV DESC',\n",
+ " 'PRODUTO LANÇAMENTO',\n",
+ " 'DESCRIÇÃO DO LANÇAMENTO',\n",
+ " 'MARCA',\n",
+ " 'CATEGORIA',\n",
+ " 'MECANICA CONSUMIDOR',\n",
+ " '% CONSUMIDOR',\n",
+ " 'MECANICA REVENDEDOR',\n",
+ " '% REVENDEDOR',\n",
+ " 'TIPO DE PRODUTO',\n",
+ " 'IAF',\n",
+ " 'FOCO',\n",
+ " 'SKU',\n",
+ " 'PRODUTO SIMILAR',\n",
+ " 'DESCRIÇÃO SIMILAR',\n",
+ " 'CICLO SIMILAR',\n",
+ " 'VENDAS CICLO LANÇAMENTO',\n",
+ " 'C-4',\n",
+ " 'C-3',\n",
+ " 'C-2',\n",
+ " 'C-1',\n",
+ " 'VENDAS CICLO ATUAL',\n",
+ " 'PICO VENDAS SIMILAR ULTIMOS 6 CICLOS',\n",
+ " 'PV GINSENG'])\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a3e80cb4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['SUGESTÃO METASELLIN'] = ''\n",
+ "df_final['SUGESTÃO ABASTECIMENTO'] = ''\n",
+ "df_final['SUGESTÃO COMERCIAL'] = ''\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2df3e2e9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.to_excel(r'C:\\Users\\joao.herculano\\Documents\\sugest.xlsx',index=False)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/Ruptura_Projetada/ruptura projetada 23.05.py b/Ruptura_Projetada/ruptura projetada 23.05.py
new file mode 100644
index 0000000..a7be12b
--- /dev/null
+++ b/Ruptura_Projetada/ruptura projetada 23.05.py
@@ -0,0 +1,221 @@
+import smtplib
+import ssl
+import psycopg2
+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 = psycopg2.connect(
+ host=config['banco']['host'],
+ port="5432",
+ database="ginseng",
+ user=config['banco']['user'],
+ password=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 = '' 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 = false THEN 'REGULAR' ELSE 'CRITICO' END AS status_item
+FROM Draft
+WHERE isproductdeactivated = false 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"""
+
+
+ {boa}
+ Compartilhamos o relatório de ruptura projetada, com o objetivo de monitorar os itens com maior risco de ruptura nos próximos dias.
+ O relatório a seguir apresenta as seguintes informações:
+
+ - Estoque atual;
+ - Trânsito;
+ - Ruptura projetada;
+ - Maior excesso por estado, assim como o PDV que o possui.
+
+ 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.
+ Importante: O relatório está em processo de desenvolvimento e pode sofrer mudanças futuras no layout. Ficamos à disposição para esclarecer quaisquer dúvidas.
+
+
+
+
+
+"""
+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.")
\ No newline at end of file
diff --git a/promoção/promoção_EUD_ciclo07.ipynb b/promoção/promoção_EUD_ciclo07.ipynb
new file mode 100644
index 0000000..676ec44
--- /dev/null
+++ b/promoção/promoção_EUD_ciclo07.ipynb
@@ -0,0 +1,1644 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "import numpy as np \n",
+ "import glob\n",
+ "import os "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# O QUE PRECISA PRA RODAR ESSE CÓDIGO:\n",
+ "\n",
+ "# Arquivo Draft\n",
+ "# Arquivo Estoque\n",
+ "# Arquivo BI preço\n",
+ "# Arquivo pdv\n",
+ "# Arquivo Calendario\n",
+ "# Arquivo tabela de compra\n",
+ "\n",
+ "#Atualizar o nome da marca no filtro do estoque."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\joao.herculano\\AppData\\Local\\Temp\\ipykernel_90044\\119945099.py:10: DtypeWarning: Columns (7) have mixed types. Specify dtype option on import or set low_memory=False.\n",
+ " df_draft = pd.concat([pd.read_csv(file) for file in csv_files], ignore_index=True)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "(115164, 46)"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Caminho onde estão as subpastas com os arquivos CSV\n",
+ "\n",
+ "# Set the path to the folder containing CSV files\n",
+ "folder_path = r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\EUDORA\\202510\\DRAFT_PDVS_SEM\" # arquivo dos drafts\n",
+ "\n",
+ "# Pattern to match all CSV files\n",
+ "csv_files = glob.glob(os.path.join(folder_path, '*.csv'))\n",
+ "\n",
+ "# Read and concat all CSVs\n",
+ "df_draft = pd.concat([pd.read_csv(file) for file in csv_files], ignore_index=True)\n",
+ "\n",
+ "\n",
+ "df_draft.shape\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft['match'] = 1 \n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "\n",
+ "# Caminho onde estão as subpastas com os arquivos CSV\n",
+ "pasta_entrada = r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\EUDORA\\202510\\estoque\"\n",
+ "\n",
+ "# Lista todas as subpastas dentro de \"ESTOQUE\"\n",
+ "subpastas = [os.path.join(pasta_entrada, d) for d in os.listdir(pasta_entrada) if os.path.isdir(os.path.join(pasta_entrada, d))]\n",
+ "\n",
+ "df_list = []\n",
+ "\n",
+ "# Percorre todas as subpastas\n",
+ "for subpasta in subpastas:\n",
+ " arquivos = [f for f in os.listdir(subpasta) if f.endswith(\".csv\")]\n",
+ " nome_pasta = os.path.basename(subpasta) # Obtém o nome da pasta\n",
+ "\n",
+ " for arquivo in arquivos:\n",
+ " caminho_arquivo = os.path.join(subpasta, arquivo)\n",
+ " try:\n",
+ " df = pd.read_csv(caminho_arquivo, encoding=\"utf-8\", low_memory=False) # Melhor para grandes volumes de dados\n",
+ " df[\"Arquivo_Origem\"] = arquivo # Adiciona o nome do arquivo de origem\n",
+ " df[\"Pasta_Origem\"] = nome_pasta # Adiciona o nome da pasta de origem\n",
+ " df_list.append(df)\n",
+ " except Exception as e:\n",
+ " print(f\"Erro ao ler o arquivo {arquivo}: {e}\")\n",
+ "\n",
+ "if df_list:\n",
+ " df_estoque = pd.concat(df_list, ignore_index=True)\n",
+ "\n",
+ "df_estoque['PDV'] = df_estoque['PDV'].astype(str)\n",
+ "\n",
+ "df_estoque['SKU_FINAL'] = np.where(df_estoque['SKU_PARA'] == \"-\", df_estoque['SKU'], df_estoque['SKU_PARA'])\n",
+ "\n",
+ "df_estoque['SKU_FINAL']=df_estoque['SKU_FINAL'].astype(str)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "c:\\Users\\joao.herculano\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\openpyxl\\styles\\stylesheet.py:237: UserWarning: Workbook contains no default style, apply openpyxl's default\n",
+ " warn(\"Workbook contains no default style, apply openpyxl's default\")\n"
+ ]
+ }
+ ],
+ "source": [
+ "df_bi_preco = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\EUDORA\\202510\\preçobi\\TABELA DE PREÇOS (4).xlsx\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['SKU1', 'SKU2', 'Descrição', 'MARCA', 'CATEGORIA', 'LINHA', 'UF',\n",
+ " 'Tipo Preço', 'PC', 'PV'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_bi_preco.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\EUDORA\\202509\\arquivos pra gerar\\pdvs\\PDV_ATT.xlsx\")\n",
+ "df_pdv = df_pdv.rename(columns={'DESCRIÇÃO':'DESCRIÇÃO PDV'})\n",
+ "df_pdv = df_pdv.drop(columns=['REGIÃO', 'ESTADO','CIDADE','GESTÃO', 'SUPERVISOR', 'STATUS'])\n",
+ "df_pdv['PDV'] = df_pdv['PDV DESC'].str.split(\"-\").str[0].str.strip()\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#ignorando a PDV que ainda não está online\n",
+ "df_pdv = df_pdv[df_pdv['DESCRIÇÃO PDV'] != '23813-COMERCIO-HIB VALENTE']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv['MATCH'] = 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**ALTERAR NOME DA COLUNA \"ARQUIVO_ORIGEM\" PARA UMA DAS OPÇÕES ABAIXO:**\n",
+ "\n",
+ "*BOT.csv* \n",
+ "\n",
+ "*EUD.csv*\n",
+ "\n",
+ "*QDB.csv*"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_estoque = df_estoque[df_estoque['Arquivo_Origem']== \"EUD.csv\"]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "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\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "calendario['Date'] = pd.to_datetime(calendario['Date'])\n",
+ "\n",
+ "# Get today (normalized to midnight)\n",
+ "today = pd.Timestamp(\"today\").normalize()\n",
+ "\n",
+ "calendario['NUM_CICLO'] = calendario['Ciclo'].str[-2:].astype(int)\n",
+ "calendario['ANO_CICLO'] = calendario['Ciclo'].str[0:5]\n",
+ "\n",
+ "\n",
+ "calendario = calendario[calendario['MARCA'] == \"EUDORA\"]\n",
+ "\n",
+ "calendario['CICLOMAIS2'] = calendario['ANO_CICLO'].astype(str) + (calendario['NUM_CICLO'].astype(int) + 2).astype(str).str.zfill(2)\n",
+ "\n",
+ "ciclo_mais2 = calendario[calendario['Date'].dt.normalize() == today]['CICLOMAIS2'].iloc[0]\n",
+ "\n",
+ "\n",
+ "# Filter rows where date matches today\n",
+ "filtered_calendario = calendario[calendario['Ciclo'] == ciclo_mais2][:1]\n",
+ "\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['INICIO CICLO'].iloc[0] - today\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['dias_ate_inicio'].dt.days.astype(int)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Ciclo | \n",
+ " INICIO CICLO | \n",
+ " FIM CICLO | \n",
+ " DURAÇÃO | \n",
+ " MARCA | \n",
+ " Date | \n",
+ " NUM_CICLO | \n",
+ " ANO_CICLO | \n",
+ " CICLOMAIS2 | \n",
+ " dias_ate_inicio | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 2220 | \n",
+ " C202510 | \n",
+ " 2025-06-25 | \n",
+ " 2025-07-15 | \n",
+ " 21 | \n",
+ " EUDORA | \n",
+ " 2025-06-25 | \n",
+ " 10 | \n",
+ " C2025 | \n",
+ " C202512 | \n",
+ " 28 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Ciclo INICIO CICLO FIM CICLO DURAÇÃO MARCA Date NUM_CICLO \\\n",
+ "2220 C202510 2025-06-25 2025-07-15 21 EUDORA 2025-06-25 10 \n",
+ "\n",
+ " ANO_CICLO CICLOMAIS2 dias_ate_inicio \n",
+ "2220 C2025 C202512 28 "
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "filtered_calendario"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "filtered_calendario['match'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv['UF'] = np.where(df_pdv['UF'] == 'VDC','BA',df_pdv['UF'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\EUDORA\\202510\\tabela promo\\Tabela-de-Promocoes_C10_att-1747056411627.xlsx.xlsx\")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = df_tabela[[ 'Ciclo', 'Veiculo',\n",
+ " 'Tipo de Promoção', 'Estratégia de Promoção',\n",
+ " 'Tipo_mecanica', 'Promo Período Limitado?',\n",
+ " 'EAM', 'Categoria',\n",
+ " 'Cód. Combo', 'Código do Item',\n",
+ " 'Descrição do Item', 'Chamada Promocional',\n",
+ " 'Valor do Guia', 'Preço Promocionado',\n",
+ " '% de Desconto', 'RE compra por',\n",
+ " 'RE Vende por', 'RE lucra (R$)']]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela['Código do Item'] = df_tabela['Código do Item'].astype(str).str.replace(\".0\",\"\",regex=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela['MATCH'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = pd.merge(left=df_tabela,right=df_pdv[['PDV','MATCH','UF','DESCRIÇÃO PDV','ANALISTA']],on='MATCH',how='inner')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = df_tabela.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['Ciclo', 'Veiculo', 'Tipo de Promoção', 'Estratégia de Promoção',\n",
+ " 'Tipo_mecanica', 'Promo Período Limitado?', 'EAM', 'Categoria',\n",
+ " 'Cód. Combo', 'Código do Item', 'Descrição do Item',\n",
+ " 'Chamada Promocional', 'Valor do Guia', 'Preço Promocionado',\n",
+ " '% de Desconto', 'RE compra por', 'RE Vende por', 'RE lucra (R$)',\n",
+ " 'MATCH', 'PDV', 'UF', 'DESCRIÇÃO PDV', 'ANALISTA'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_tabela.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft['PDV'] = df_draft['PDV'].astype(str)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft['SKU'] = df_draft['SKU'].astype(str)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela['Código do Item'] = df_tabela['Código do Item'].astype('str')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1518, 69)"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final = pd.merge(left=df_draft,right=df_tabela,right_on=['Código do Item','PDV'],left_on=['SKU','PDV'],how='inner')\n",
+ "df_final.shape "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1518, 74)"
+ ]
+ },
+ "execution_count": 28,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=filtered_calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO','match','dias_ate_inicio']], on='match',how='inner')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1518, 74)"
+ ]
+ },
+ "execution_count": 29,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#df_final = pd.merge(left=df_final,right=df_pdv[['PDV', 'CANAL', 'DESCRIÇÃO PDV', 'PDV DESC','UF', 'MARCA', 'ANALISTA']],on = 'PDV',how='inner')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1831, 77)"
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['SKU'] = df_final['SKU'].astype(str) \n",
+ "df_final['PDV'] = df_final['PDV'].astype(str) \n",
+ "df_final = pd.merge(left=df_final,right=df_estoque[['SKU_FINAL', 'DDV PREVISTO', 'COBERTURA ATUAL','PDV']],right_on=['PDV','SKU_FINAL'],left_on=['PDV','SKU'],how='left')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1831, 82)"
+ ]
+ },
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\n",
+ "df_bi_preco['SKU2'] = df_bi_preco['SKU2'].astype(str).str.replace('.0','',regex=False) \n",
+ "\n",
+ "df_final = pd.merge(left=df_final,right=df_bi_preco[['SKU1', 'SKU2', 'UF','Tipo Preço', 'PC', 'PV']],right_on=['UF','SKU2'],left_on=['UF','SKU'],how='left')\n",
+ "df_final.shape "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1831, 86)"
+ ]
+ },
+ "execution_count": 32,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_bi_preco['SKU1'] = df_bi_preco['SKU1'].astype(str).str.replace('.0','',regex=False) \n",
+ "\n",
+ "df_final = pd.merge(left=df_final,right=df_bi_preco[['SKU1', 'SKU2', 'UF', 'PC', 'PV']],right_on=['UF','SKU1'],left_on=['UF','SKU'],how='left')\n",
+ "df_final.shape "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#df_bi_preco = df_bi_preco[df_bi_preco['MARCA']=='EUDORA']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PRECO DE COMPRA'] = np.where(~df_final['PC_x'].isna(),df_final['PC_x'],df_final['PC_y'])\n",
+ "\n",
+ "df_final['PRECO DE VENDA'] = np.where(~df_final['PV_x'].isna(),df_final['PV_x'],df_final['PV_y'])\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_estoque = df_estoque.rename(columns={'SKU_FINAL':'SKU_PARA_VALIDACAO'})\n",
+ "df_final = pd.merge( left= df_final, right = df_estoque[['SKU_PARA_VALIDACAO','Arquivo_Origem']], left_on= 'SKU', right_on='SKU_PARA_VALIDACAO', how='left')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PDV', 'Classe', 'SKU', 'Descrição', 'Categoria_x', 'Subcategoria',\n",
+ " 'Lançamento', 'Desativação', 'Histórico de Vendas do Ciclo 202408',\n",
+ " 'Histórico de Vendas do Ciclo 202409',\n",
+ " 'Histórico de Vendas do Ciclo 202410',\n",
+ " 'Histórico de Vendas do Ciclo 202411',\n",
+ " 'Histórico de Vendas do Ciclo 202412',\n",
+ " 'Histórico de Vendas do Ciclo 202413',\n",
+ " 'Histórico de Vendas do Ciclo 202414',\n",
+ " 'Histórico de Vendas do Ciclo 202415',\n",
+ " 'Histórico de Vendas do Ciclo 202416',\n",
+ " 'Histórico de Vendas do Ciclo 202417',\n",
+ " 'Histórico de Vendas do Ciclo 202501',\n",
+ " 'Histórico de Vendas do Ciclo 202502',\n",
+ " 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo 202507',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'Dias sem venda',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1',\n",
+ " 'Promoção Próximo Ciclo', 'Promoção Próximo Ciclo + 1', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Pedido Pendente',\n",
+ " 'Compra inteligente semanal/Sugestão de compra',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'Carteira Bloqueada Para Novos Pedidos',\n",
+ " 'Planograma', 'Quantidade por caixa', 'Preço Sell In', 'Quantidade',\n",
+ " 'Item analisado', 'Histórico de Vendas do Ciclo 202407', 'match',\n",
+ " 'Ciclo_x', 'Veiculo', 'Tipo de Promoção', 'Estratégia de Promoção',\n",
+ " 'Tipo_mecanica', 'Promo Período Limitado?', 'EAM', 'Categoria_y',\n",
+ " 'Cód. Combo', 'Código do Item', 'Descrição do Item',\n",
+ " 'Chamada Promocional', 'Valor do Guia', 'Preço Promocionado',\n",
+ " '% de Desconto', 'RE compra por', 'RE Vende por', 'RE lucra (R$)',\n",
+ " 'MATCH', 'UF', 'DESCRIÇÃO PDV', 'ANALISTA', 'Ciclo_y', 'INICIO CICLO',\n",
+ " 'FIM CICLO', 'DURAÇÃO', 'dias_ate_inicio', 'SKU_FINAL', 'DDV PREVISTO',\n",
+ " 'COBERTURA ATUAL', 'SKU1_x', 'SKU2_x', 'Tipo Preço', 'PC_x', 'PV_x',\n",
+ " 'SKU1_y', 'SKU2_y', 'PC_y', 'PV_y', 'PRECO DE COMPRA', 'PRECO DE VENDA',\n",
+ " 'SKU_PARA_VALIDACAO', 'Arquivo_Origem'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 36,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns=['Desativação','Lançamento',\n",
+ "'Promoção Próximo Ciclo',\n",
+ "'Compra inteligente semanal/Sugestão de compra',\n",
+ "'Planograma',\n",
+ "'Carteira Bloqueada Para Novos Pedidos',\n",
+ "'Preço Sell In',\n",
+ "'Quantidade',\n",
+ "'Item analisado',\n",
+ "'Promoção Próximo Ciclo + 1',\n",
+ "'SKU_PARA_VALIDACAO',\n",
+ "'Ciclo_y'\n",
+ "])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(101091, 80)"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['PDV_SKU'] = df_final['PDV'].astype(str) + df_final['SKU'].astype(str) \n",
+ "df_final['UFPRODUTO'] = df_final['UF'].astype(str) + df_final['SKU'].astype(str)\n",
+ "\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Projeção Próximo Ciclo + 1\n",
+ "0 11230\n",
+ "1 6095\n",
+ "2 5693\n",
+ "4 5037\n",
+ "3 4691\n",
+ " ... \n",
+ "60 13\n",
+ "575 13\n",
+ "197 13\n",
+ "351 13\n",
+ "118 13\n",
+ "Name: count, Length: 129, dtype: int64"
+ ]
+ },
+ "execution_count": 39,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['Projeção Próximo Ciclo + 1'].value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PROJEÇÃO DO CICLO PROMOCIONADO'] =df_final['Projeção Próximo Ciclo + 1'] - df_final['Projeção Próximo Ciclo'] # projeção do ciclo em estudo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "PROJEÇÃO DO CICLO PROMOCIONADO\n",
+ "0 11243\n",
+ "1 7913\n",
+ "2 7288\n",
+ "4 5839\n",
+ "5 5191\n",
+ " ... \n",
+ "175 13\n",
+ "312 13\n",
+ "108 13\n",
+ "69 13\n",
+ "161 13\n",
+ "Name: count, Length: 115, dtype: int64"
+ ]
+ },
+ "execution_count": 41,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['PROJEÇÃO DO CICLO PROMOCIONADO'].value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['Data Prevista Regularização'] = df_final['Data Prevista Regularização'].astype(str).replace('0','REGULAR')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.rename(columns={'Arquivo_Origem': 'MARCA'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "np.int64(0)"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final = df_final[~df_final['MARCA'].isna()]\n",
+ "df_final['MARCA'].isna().sum()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(1822, 81)"
+ ]
+ },
+ "execution_count": 45,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final = df_final.drop_duplicates()\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 46,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PDV', 'Classe', 'SKU', 'Descrição', 'Categoria_x', 'Subcategoria',\n",
+ " 'Histórico de Vendas do Ciclo 202408',\n",
+ " 'Histórico de Vendas do Ciclo 202409',\n",
+ " 'Histórico de Vendas do Ciclo 202410',\n",
+ " 'Histórico de Vendas do Ciclo 202411',\n",
+ " 'Histórico de Vendas do Ciclo 202412',\n",
+ " 'Histórico de Vendas do Ciclo 202413',\n",
+ " 'Histórico de Vendas do Ciclo 202414',\n",
+ " 'Histórico de Vendas do Ciclo 202415',\n",
+ " 'Histórico de Vendas do Ciclo 202416',\n",
+ " 'Histórico de Vendas do Ciclo 202417',\n",
+ " 'Histórico de Vendas do Ciclo 202501',\n",
+ " 'Histórico de Vendas do Ciclo 202502',\n",
+ " 'Histórico de Vendas do Ciclo 202503',\n",
+ " 'Histórico de Vendas do Ciclo 202504',\n",
+ " 'Histórico de Vendas do Ciclo 202505',\n",
+ " 'Histórico de Vendas do Ciclo 202506',\n",
+ " 'Histórico de Vendas do Ciclo 202507',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'Dias sem venda',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Pedido Pendente',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'Quantidade por caixa',\n",
+ " 'Histórico de Vendas do Ciclo 202407', 'match', 'Ciclo_x', 'Veiculo',\n",
+ " 'Tipo de Promoção', 'Estratégia de Promoção', 'Tipo_mecanica',\n",
+ " 'Promo Período Limitado?', 'EAM', 'Categoria_y', 'Cód. Combo',\n",
+ " 'Código do Item', 'Descrição do Item', 'Chamada Promocional',\n",
+ " 'Valor do Guia', 'Preço Promocionado', '% de Desconto', 'RE compra por',\n",
+ " 'RE Vende por', 'RE lucra (R$)', 'MATCH', 'UF', 'DESCRIÇÃO PDV',\n",
+ " 'ANALISTA', 'INICIO CICLO', 'FIM CICLO', 'DURAÇÃO', 'dias_ate_inicio',\n",
+ " 'SKU_FINAL', 'DDV PREVISTO', 'COBERTURA ATUAL', 'SKU1_x', 'SKU2_x',\n",
+ " 'Tipo Preço', 'PC_x', 'PV_x', 'SKU1_y', 'SKU2_y', 'PC_y', 'PV_y',\n",
+ " 'PRECO DE COMPRA', 'PRECO DE VENDA', 'MARCA', 'PDV_SKU', 'UFPRODUTO',\n",
+ " 'PROJEÇÃO DO CICLO PROMOCIONADO'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define list of target columns\n",
+ "sales_2024_cols = df_final.columns[6:24]\n",
+ "# Create a new column with the row-wise max\n",
+ "df_final['PICO DE VENDAS 2024'] = df_final[sales_2024_cols].max(axis=1)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "vendas_6_meses = df_final.columns[17:23]\n",
+ "\n",
+ "df_final['Pico Vendas Ultimos 6 ciclos'] = df_final[vendas_6_meses].max(axis=1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "-0.14227416063810053\n"
+ ]
+ }
+ ],
+ "source": [
+ "CRESCIMENTO = (df_final[df_final.columns[17]].sum() - df_final[df_final.columns[22]].sum())/df_final[df_final.columns[17]].sum() \n",
+ "print(CRESCIMENTO)\n",
+ "\n",
+ "df_final['CRESCIMENTO'] = .2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mesmo_ciclo_ano_passado = df_final.columns[8:9]\n",
+ "ciclo_ano_passado = df_final.columns[8:9].str.split(\" \")[0][-1]\n",
+ "df_final[ciclo_ano_passado] = df_final[mesmo_ciclo_ano_passado]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'202410'"
+ ]
+ },
+ "execution_count": 51,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns[8:9].str.split(\" \")[0][-1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "vendas_todos_historicos = df_final.columns[17:23]\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = df_final[vendas_todos_historicos].median(axis=1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "np.float64(-0.14227416063810053)"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "CRESCIMENTO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PV GINSENG'] = np.where(df_final['CRESCIMENTO'] * df_final[ciclo_ano_passado] + df_final[ciclo_ano_passado] < df_final['MEDIANA DO HISTÓRICO'],\n",
+ " round(df_final['CRESCIMENTO'] * df_final['MEDIANA DO HISTÓRICO']+ df_final['MEDIANA DO HISTÓRICO'],0), \n",
+ " round(df_final['CRESCIMENTO']*df_final[ciclo_ano_passado]+df_final[ciclo_ano_passado],0))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 55,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.rename(columns={df_final.columns[18]: \"C-4\", df_final.columns[19]: \"C-3\",df_final.columns[20]: \"C-2\",df_final.columns[21]: \"C-1\"})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.drop(columns=df_final.columns[6:18], inplace=True)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# List all columns except the two\n",
+ "cols_to_group_by = df_final.columns.difference(['DDV PREVISTO', 'COBERTURA ATUAL'])\n",
+ "\n",
+ "# Group and aggregate\n",
+ "df_final_dedup = (\n",
+ " df_final\n",
+ " .groupby(list(cols_to_group_by), dropna=False)[['DDV PREVISTO', 'COBERTURA ATUAL']]\n",
+ " .max()\n",
+ " .reset_index()\n",
+ ")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 58,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0 0\n",
+ "24 0\n",
+ "48 0\n",
+ "72 9\n",
+ "96 0\n",
+ " ..\n",
+ "101026 0\n",
+ "101039 2\n",
+ "101052 1\n",
+ "101065 1\n",
+ "101078 7\n",
+ "Name: Compra inteligente Próximo Ciclo, Length: 1822, dtype: int64"
+ ]
+ },
+ "execution_count": 58,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final['Compra inteligente Próximo Ciclo']\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 59,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['DDV PREVISTO'] = df_final_dedup['DDV PREVISTO'].fillna(0.01)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['DDV PREVISTO'] = np.where(\n",
+ " df_final_dedup['DDV PREVISTO'] == 0,\n",
+ " 0.01,\n",
+ " df_final_dedup['DDV PREVISTO']\n",
+ ")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 61,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "DDV PREVISTO\n",
+ "0.01 302\n",
+ "0.02 120\n",
+ "0.04 109\n",
+ "0.07 50\n",
+ "0.13 36\n",
+ " ... \n",
+ "1.56 1\n",
+ "3.58 1\n",
+ "3.75 1\n",
+ "6.98 1\n",
+ "10.96 1\n",
+ "Name: count, Length: 195, dtype: int64"
+ ]
+ },
+ "execution_count": 61,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final_dedup['DDV PREVISTO'].value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['EST PROJE FINAL CICLO ATUAL'] = (df_final_dedup['Estoque Atual'] + df_final_dedup['Estoque em Transito']) - round(df_final_dedup['dias_ate_inicio'] * df_final_dedup['DDV PREVISTO'],0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['EST PROJE FINAL CICLO ATUAL'] = np.where(df_final_dedup['EST PROJE FINAL CICLO ATUAL']<0,0,df_final_dedup['EST PROJE FINAL CICLO ATUAL'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['% de Desconto', '202410', 'ANALISTA', 'C-1', 'C-2', 'C-3', 'C-4',\n",
+ " 'CRESCIMENTO', 'Categoria_x', 'Categoria_y', 'Chamada Promocional',\n",
+ " 'Ciclo_x', 'Classe', 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Cód. Combo', 'Código do Item',\n",
+ " 'DESCRIÇÃO PDV', 'DURAÇÃO', 'Data Prevista Regularização', 'Descrição',\n",
+ " 'Descrição do Item', 'Dias sem venda', 'EAM', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Estratégia de Promoção', 'FIM CICLO',\n",
+ " 'Histórico de Vendas do Ciclo 202407',\n",
+ " 'Histórico de Vendas do Ciclo 202507',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'INICIO CICLO', 'Item Desativado',\n",
+ " 'MARCA', 'MATCH', 'MEDIANA DO HISTÓRICO', 'PC_x', 'PC_y', 'PDV',\n",
+ " 'PDV_SKU', 'PICO DE VENDAS 2024', 'PRECO DE COMPRA', 'PRECO DE VENDA',\n",
+ " 'PROJEÇÃO DO CICLO PROMOCIONADO', 'PV GINSENG', 'PV_x', 'PV_y',\n",
+ " 'Pedido Pendente', 'Pico Vendas Ultimos 6 ciclos', 'Preço Promocionado',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1',\n",
+ " 'Promo Período Limitado?', 'Quantidade por caixa', 'RE Vende por',\n",
+ " 'RE compra por', 'RE lucra (R$)', 'SKU', 'SKU1_x', 'SKU1_y', 'SKU2_x',\n",
+ " 'SKU2_y', 'SKU_FINAL', 'Subcategoria', 'Tipo Preço', 'Tipo de Promoção',\n",
+ " 'Tipo_mecanica', 'UF', 'UFPRODUTO', 'Valor do Guia', 'Veiculo',\n",
+ " 'dias_ate_inicio', 'match', 'DDV PREVISTO', 'COBERTURA ATUAL',\n",
+ " 'EST PROJE FINAL CICLO ATUAL'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 64,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final_dedup.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['VENDAS R$ PV GINSENG'] = df_final_dedup['PRECO DE VENDA'] * df_final_dedup['PV GINSENG']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup = df_final_dedup.rename(columns={'Ciclo_x':'Ciclo'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['SUGESTÃO ABTASTECIMENTO\t'] = ''\n",
+ "df_final_dedup['VENDAS R$ ABASTECIMENTO'] = ''\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['RBV 202406'] = df_final_dedup['PRECO DE VENDA'] * df_final_dedup[ciclo_ano_passado] "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['COB PROJETADA'] = np.where(\n",
+ " df_final_dedup['DDV PREVISTO'] != 0,\n",
+ " (df_final_dedup['EST PROJE FINAL CICLO ATUAL'] + df_final_dedup['PV GINSENG']) / df_final_dedup['DDV PREVISTO'],\n",
+ " 999)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup.drop(columns=['dias_ate_inicio','SKU_FINAL','Projeção Próximo Ciclo + 1',\n",
+ " 'CRESCIMENTO'],inplace=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['MARCA'] = df_final_dedup['MARCA'].str.replace('.csv','',regex=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 72,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['COB PROJETADA'] = df_final_dedup['COB PROJETADA'].fillna(999)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['% de Desconto', '202410', 'ANALISTA', 'C-1', 'C-2', 'C-3', 'C-4',\n",
+ " 'Categoria_x', 'Categoria_y', 'Chamada Promocional', 'Ciclo', 'Classe',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Cód. Combo', 'Código do Item',\n",
+ " 'DESCRIÇÃO PDV', 'DURAÇÃO', 'Data Prevista Regularização', 'Descrição',\n",
+ " 'Descrição do Item', 'Dias sem venda', 'EAM', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Estratégia de Promoção', 'FIM CICLO',\n",
+ " 'Histórico de Vendas do Ciclo 202407',\n",
+ " 'Histórico de Vendas do Ciclo 202507',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'INICIO CICLO', 'Item Desativado',\n",
+ " 'MARCA', 'MATCH', 'MEDIANA DO HISTÓRICO', 'PC_x', 'PC_y', 'PDV',\n",
+ " 'PDV_SKU', 'PICO DE VENDAS 2024', 'PRECO DE COMPRA', 'PRECO DE VENDA',\n",
+ " 'PROJEÇÃO DO CICLO PROMOCIONADO', 'PV GINSENG', 'PV_x', 'PV_y',\n",
+ " 'Pedido Pendente', 'Pico Vendas Ultimos 6 ciclos', 'Preço Promocionado',\n",
+ " 'Projeção Próximo Ciclo', 'Promo Período Limitado?',\n",
+ " 'Quantidade por caixa', 'RE Vende por', 'RE compra por',\n",
+ " 'RE lucra (R$)', 'SKU', 'SKU1_x', 'SKU1_y', 'SKU2_x', 'SKU2_y',\n",
+ " 'Subcategoria', 'Tipo Preço', 'Tipo de Promoção', 'Tipo_mecanica', 'UF',\n",
+ " 'UFPRODUTO', 'Valor do Guia', 'Veiculo', 'match', 'DDV PREVISTO',\n",
+ " 'COBERTURA ATUAL', 'EST PROJE FINAL CICLO ATUAL',\n",
+ " 'VENDAS R$ PV GINSENG', 'SUGESTÃO ABTASTECIMENTO\\t',\n",
+ " 'VENDAS R$ ABASTECIMENTO', 'RBV 202406', 'COB PROJETADA'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 73,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final_dedup.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'EUD'"
+ ]
+ },
+ "execution_count": 74,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "marca_promo = df_estoque['Arquivo_Origem'].iloc[0].replace('.csv','')\n",
+ "marca_promo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PDV', 'Classe', 'SKU', 'Descrição', 'Categoria_x', 'Subcategoria',\n",
+ " 'C-4', 'C-3', 'C-2', 'C-1', 'Histórico de Vendas do Ciclo 202507',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'Dias sem venda',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1', 'Estoque Atual',\n",
+ " 'Estoque em Transito', 'Pedido Pendente',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'Quantidade por caixa',\n",
+ " 'Histórico de Vendas do Ciclo 202407', 'match', 'Ciclo_x', 'Veiculo',\n",
+ " 'Tipo de Promoção', 'Estratégia de Promoção', 'Tipo_mecanica',\n",
+ " 'Promo Período Limitado?', 'EAM', 'Categoria_y', 'Cód. Combo',\n",
+ " 'Código do Item', 'Descrição do Item', 'Chamada Promocional',\n",
+ " 'Valor do Guia', 'Preço Promocionado', '% de Desconto', 'RE compra por',\n",
+ " 'RE Vende por', 'RE lucra (R$)', 'MATCH', 'UF', 'DESCRIÇÃO PDV',\n",
+ " 'ANALISTA', 'INICIO CICLO', 'FIM CICLO', 'DURAÇÃO', 'dias_ate_inicio',\n",
+ " 'SKU_FINAL', 'DDV PREVISTO', 'COBERTURA ATUAL', 'SKU1_x', 'SKU2_x',\n",
+ " 'Tipo Preço', 'PC_x', 'PV_x', 'SKU1_y', 'SKU2_y', 'PC_y', 'PV_y',\n",
+ " 'PRECO DE COMPRA', 'PRECO DE VENDA', 'MARCA', 'PDV_SKU', 'UFPRODUTO',\n",
+ " 'PROJEÇÃO DO CICLO PROMOCIONADO', 'PICO DE VENDAS 2024',\n",
+ " 'Pico Vendas Ultimos 6 ciclos', 'CRESCIMENTO', '202410',\n",
+ " 'MEDIANA DO HISTÓRICO', 'PV GINSENG'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 75,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_final.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 76,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Passo 1: ordenar\n",
+ "df_sorted = df_final.sort_values(by='% de Desconto', ascending=False)\n",
+ "\n",
+ "# Passo 2: obter até 3 chamadas promocionais por grupo\n",
+ "promo_grouped = (\n",
+ " df_sorted.groupby(['SKU', 'PDV'])['Chamada Promocional']\n",
+ " .apply(lambda x: x.dropna().unique()[:3])\n",
+ " .apply(pd.Series)\n",
+ " .rename(columns={0: 'MECÂNICA 1', 1: 'MECÂNICA 2', 2: 'MECÂNICA 3'})\n",
+ " .reset_index()\n",
+ ")\n",
+ "\n",
+ "# Passo 3: base com maior % de desconto\n",
+ "df_base = df_sorted.drop_duplicates(subset=['SKU', 'PDV'], keep='first').reset_index(drop=True)\n",
+ "\n",
+ "# Passo 4: merge com chamadas pivotadas\n",
+ "df_merged = df_base.merge(promo_grouped, on=['SKU', 'PDV'], how='left')\n",
+ "\n",
+ "# Corrigir dicionário: agrupar e pegar o primeiro valor válido por (SKU, PDV)\n",
+ "# Passo 1: dicionário com Cód. Combo único por SKU+PDV\n",
+ "cod_combo_map = (\n",
+ " df_final\n",
+ " .dropna(subset=['Cód. Combo'])\n",
+ " .groupby(['SKU', 'PDV'])['Cód. Combo']\n",
+ " .first()\n",
+ " .to_dict()\n",
+ ")\n",
+ "\n",
+ "# Passo 2: gerar Series com os valores mapeados\n",
+ "cod_combo_series = df_merged.set_index(['SKU', 'PDV']).index.map(cod_combo_map)\n",
+ "\n",
+ "# Passo 3: substituir os valores diretamente\n",
+ "df_merged['Cód. Combo'] = cod_combo_series.where(cod_combo_series.notna(), df_merged['Cód. Combo'])\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 77,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Columns to bring up front\n",
+ "priority_cols = [\n",
+ " 'PDV_SKU',\t'SKU',\t'MARCA','INICIO CICLO',\n",
+ " 'FIM CICLO',\t'DURAÇÃO','dias_ate_inicio',\t\n",
+ " 'UFPRODUTO',\t'Item Desativado',\t'Data Prevista Regularização',\n",
+ " 'ANALISTA',\t'UF',\t'PDV',\t'DESCRIÇÃO PDV',\t'Classe',\n",
+ " 'Descrição','MECÂNICA 1',\n",
+ " 'MECÂNICA 2', 'MECÂNICA 3','Estoque Atual',\t'COBERTURA ATUAL',\n",
+ " 'Estoque em Transito',\t'Pedido Pendente',\t'PICO DE VENDAS 2024','Pico Vendas Ultimos 6 ciclos',\n",
+ " 'C-4',\t'C-3',\t'C-2',\t'C-1',\t'Histórico de Vendas do Ciclo Atual',\n",
+ " 'Dias sem venda'\n",
+ "]\n",
+ "\n",
+ "# All remaining columns\n",
+ "other_cols = [col for col in df_merged.columns if col not in priority_cols]\n",
+ "\n",
+ "# Reorder\n",
+ "df_merged = df_merged[priority_cols + other_cols]\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 78,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_merged['INICIO CICLO'] = pd.to_datetime(df_merged['INICIO CICLO'], dayfirst=True).dt.strftime('%d/%m/%Y')\n",
+ "\n",
+ "df_merged['FIM CICLO'] = pd.to_datetime(df_merged['FIM CICLO'], dayfirst=True).dt.strftime('%d/%m/%Y')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 79,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Index(['PDV_SKU', 'SKU', 'MARCA', 'INICIO CICLO', 'FIM CICLO', 'DURAÇÃO',\n",
+ " 'dias_ate_inicio', 'UFPRODUTO', 'Item Desativado',\n",
+ " 'Data Prevista Regularização', 'ANALISTA', 'UF', 'PDV', 'DESCRIÇÃO PDV',\n",
+ " 'Classe', 'Descrição', 'MECÂNICA 1', 'MECÂNICA 2', 'MECÂNICA 3',\n",
+ " 'Estoque Atual', 'COBERTURA ATUAL', 'Estoque em Transito',\n",
+ " 'Pedido Pendente', 'PICO DE VENDAS 2024',\n",
+ " 'Pico Vendas Ultimos 6 ciclos', 'C-4', 'C-3', 'C-2', 'C-1',\n",
+ " 'Histórico de Vendas do Ciclo Atual', 'Dias sem venda', 'Categoria_x',\n",
+ " 'Subcategoria', 'Histórico de Vendas do Ciclo 202507',\n",
+ " 'Projeção Próximo Ciclo', 'Projeção Próximo Ciclo + 1',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1', 'Quantidade por caixa',\n",
+ " 'Histórico de Vendas do Ciclo 202407', 'match', 'Ciclo_x', 'Veiculo',\n",
+ " 'Tipo de Promoção', 'Estratégia de Promoção', 'Tipo_mecanica',\n",
+ " 'Promo Período Limitado?', 'EAM', 'Categoria_y', 'Cód. Combo',\n",
+ " 'Código do Item', 'Descrição do Item', 'Chamada Promocional',\n",
+ " 'Valor do Guia', 'Preço Promocionado', '% de Desconto', 'RE compra por',\n",
+ " 'RE Vende por', 'RE lucra (R$)', 'MATCH', 'SKU_FINAL', 'DDV PREVISTO',\n",
+ " 'SKU1_x', 'SKU2_x', 'Tipo Preço', 'PC_x', 'PV_x', 'SKU1_y', 'SKU2_y',\n",
+ " 'PC_y', 'PV_y', 'PRECO DE COMPRA', 'PRECO DE VENDA',\n",
+ " 'PROJEÇÃO DO CICLO PROMOCIONADO', 'CRESCIMENTO', '202410',\n",
+ " 'MEDIANA DO HISTÓRICO', 'PV GINSENG'],\n",
+ " dtype='object')"
+ ]
+ },
+ "execution_count": 79,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df_merged.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 80,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_merged = df_merged.loc[:, ~df_merged.columns.str.endswith('_y')]\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 81,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_merged.columns = df_merged.columns.str.replace('_x$', '', regex=True)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 82,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_merged = df_merged.drop(columns=['Veiculo',\t'Tipo de Promoção',\t'Estratégia de Promoção','Tipo_mecanica'\n",
+ " ,'Chamada Promocional','MATCH','match', 'SKU_FINAL','Descrição do Item','CRESCIMENTO',\n",
+ " 'Cód. Combo'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 83,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_merged['SUGESTÃO ANALISTA'] = \"\"\n",
+ "\n",
+ "df_merged['SUGESTÃO COMERCIAL'] = \"\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 84,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "from openpyxl import load_workbook\n",
+ "from openpyxl.styles import PatternFill, Font\n",
+ "\n",
+ "\n",
+ "# Export to Excel\n",
+ "output_file = f'C:\\\\Users\\\\joao.herculano\\\\Documents\\\\promoção_{marca_promo}_{ciclo_mais2}_28.05.xlsx'\n",
+ "with pd.ExcelWriter(output_file, engine='openpyxl') as writer:\n",
+ " df_merged.to_excel(writer, index=False, sheet_name='Sheet1')\n",
+ "\n",
+ "# Apply styles\n",
+ "wb = load_workbook(output_file)\n",
+ "ws = wb['Sheet1']\n",
+ "\n",
+ "# Style header\n",
+ "header_fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid') # Light Blue\n",
+ "header_font = Font(color='FFFFFF', bold=True) # White & Bold\n",
+ "\n",
+ "for cell in ws[1]:\n",
+ " cell.fill = header_fill\n",
+ " cell.font = header_font\n",
+ "\n",
+ "# Style rows: gray/white alternating\n",
+ "gray_fill = PatternFill(start_color='DDDDDD', end_color='DDDDDD', fill_type='solid') # Light gray\n",
+ "\n",
+ "for i, row in enumerate(ws.iter_rows(min_row=2, max_row=ws.max_row), start=2):\n",
+ " if i % 2 == 0:\n",
+ " for cell in row:\n",
+ " cell.fill = gray_fill\n",
+ "\n",
+ "# Save styled workbook\n",
+ "wb.save(output_file)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "coisas q saem\n",
+ "\n",
+ "Lançamento\n",
+ "Subcategoria\n",
+ "Projeção Próximo Ciclo \n",
+ "Promoção Próximo Ciclo\n",
+ "Compra inteligente semanal/Sugestão de compra\n",
+ "Compra inteligente Próximo Ciclo\n",
+ "Planograma\n",
+ "Carteira Bloqueada Para Novos Pedidos\n",
+ "Quantidade por caixa\n",
+ "Preço Sell In\n",
+ "Quantidade\n",
+ "Item analisado\n",
+ "Tipo Preço\n",
+ "\n",
+ ">>>>>>>>>>>>>NAO ESTÁ PEGANDO O MERGE COM O DF_ESTOQUE\n",
+ "\n",
+ "\n",
+ "CRIAR PROJEÇÃO DE VENDA DO CICLO ATUAL\n",
+ "Compra inteligente Próximo Ciclo + 1 >>>>>>> RENAME PRA NOROMAL"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/promoção/promoção_boti_ciclo07.ipynb b/promoção/promoção_boti_ciclo07.ipynb
new file mode 100644
index 0000000..52c7b46
--- /dev/null
+++ b/promoção/promoção_boti_ciclo07.ipynb
@@ -0,0 +1,1224 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "import numpy as np \n",
+ "import glob\n",
+ "import os \n",
+ "\n",
+ "\n",
+ "from openpyxl import load_workbook\n",
+ "from openpyxl.styles import PatternFill, Font"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# O QUE PRECISA PRA RODAR ESSE CÓDIGO:\n",
+ "\n",
+ "# Arquivo Draft\n",
+ "# Arquivo Estoque\n",
+ "# Arquivo BI preço\n",
+ "# Arquivo pdv\n",
+ "# Arquivo Calendario\n",
+ "# Arquivo tabela de compra\n",
+ "\n",
+ "#Atualizar o nome da marca no filtro do estoque."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C10\\TABELA DE PEDIDOS\\Pedidos Semanais Especiais - BOT - 202510.xlsx\")\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Ação revendedor'].notna() | df_tabela['Ação consumidor'].notna()]\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Região'] == 'NNE'] \n",
+ "\n",
+ "df_tabela = df_tabela[(df_tabela['Canal'] != 'Ecomm')]\n",
+ "\n",
+ "#df_tabela['Canal'] = np.where((df_tabela['Canal'] == \"Loja\") | (df_tabela['Canal'] == \"Todos\") | (df_tabela['Canal'] == \"Loja | VD\"),\"TODOS\",\"VD\")\n",
+ "\n",
+ "df_tabela = df_tabela[(df_tabela['Categoria'] != \"EMBALAGENS\") | (df_tabela['Categoria'] != \"SUPORTE À VENDA\")]\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Tipo de pedido'] == 'Semanal']\n",
+ "\n",
+ "df_tabela = df_tabela[~df_tabela['Descrição'].str.contains('PRM')]\n",
+ "\n",
+ "df_tabela = df_tabela[df_tabela['Tipo de produto']!= 'EDICAO LIMITADA']\n",
+ "\n",
+ "df_tabela['Ação revendedor'] = np.where(df_tabela['Ação revendedor'].isna(),df_tabela['Ação consumidor'],df_tabela['Ação revendedor'])\n",
+ "\n",
+ "df_tabela['Percentual de desconto revendedor'] = np.where(df_tabela['Percentual de desconto revendedor'].isna(),df_tabela['Percentual de desconto consumidor'],df_tabela['Percentual de desconto revendedor'])\n",
+ "\n",
+ "df_tabela['MATCH'] = 1\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C09\\arquivos pra gerar\\pdvs\\PDV_ATT.xlsx\")\n",
+ "\n",
+ "df_pdv_origi = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C09\\arquivos pra gerar\\pdvs\\PDV_ATT.xlsx\")\n",
+ "\n",
+ "df_pdv = df_pdv.rename(columns={'DESCRIÇÃO':'DESCRIÇÃO PDV'})\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['STATUS']!=\"INATIVO\"]\n",
+ "\n",
+ "df_pdv = df_pdv.drop(columns=['REGIÃO', 'ESTADO','CIDADE','GESTÃO', 'STATUS'])\n",
+ "\n",
+ "df_pdv['PDV'] = df_pdv['PDV DESC'].str.split(\"-\").str[0].str.strip()\n",
+ "\n",
+ "df_pdv = df_pdv[df_pdv['CANAL']!='MTZ']\n",
+ "\n",
+ "#df_pdv['CANAL'] = np.where((df_pdv['CANAL']=='LJ')|(df_pdv['CANAL']=='HIB')|(df_pdv['CANAL']=='CD'),'TODOS','VD')\n",
+ "\n",
+ "df_pdv['MATCH'] = 1\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv = df_pdv.drop(columns=['pdv como texto','PDV DESC'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Caminho onde estão as subpastas com os arquivos CSV\n",
+ "\n",
+ "# Set the path to the folder containing CSV files\n",
+ "folder_path = r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C10\\DRAFT_PDVS_SEM_\" # arquivo dos drafts\n",
+ "\n",
+ "# Pattern to match all CSV files\n",
+ "csv_files = glob.glob(os.path.join(folder_path, '*.csv'))\n",
+ "\n",
+ "# Read and concat all CSVs\n",
+ "df_draft = pd.concat([pd.read_csv(file) for file in csv_files], ignore_index=True)\n",
+ "\n",
+ "df_draft.shape\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft = df_draft.drop(columns=['Descrição','Compra inteligente semanal/Sugestão de compra',\n",
+ " 'Compra inteligente Próximo Ciclo',\n",
+ " 'Compra inteligente Próximo Ciclo + 1','Planograma', 'Quantidade por caixa', 'Preço Sell In', 'Quantidade',\n",
+ " 'Item analisado', 'Subcategoria',\n",
+ " 'Lançamento', 'Desativação',\n",
+ " 'Promoção Próximo Ciclo', 'Categoria'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\n",
+ "\n",
+ "# Caminho onde estão as subpastas com os arquivos CSV\n",
+ "pasta_entrada = r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C10\\estoque\"\n",
+ "\n",
+ "# Lista todas as subpastas dentro de \"ESTOQUE\"\n",
+ "subpastas = [os.path.join(pasta_entrada, d) for d in os.listdir(pasta_entrada) if os.path.isdir(os.path.join(pasta_entrada, d))]\n",
+ "\n",
+ "df_list = []\n",
+ "\n",
+ "# Percorre todas as subpastas\n",
+ "for subpasta in subpastas:\n",
+ " arquivos = [f for f in os.listdir(subpasta) if f.endswith(\".csv\")]\n",
+ " nome_pasta = os.path.basename(subpasta) # Obtém o nome da pasta\n",
+ "\n",
+ " for arquivo in arquivos:\n",
+ " caminho_arquivo = os.path.join(subpasta, arquivo)\n",
+ " try:\n",
+ " df = pd.read_csv(caminho_arquivo, encoding=\"utf-8\", low_memory=False) # Melhor para grandes volumes de dados\n",
+ " df[\"Arquivo_Origem\"] = arquivo # Adiciona o nome do arquivo de origem\n",
+ " df[\"Pasta_Origem\"] = nome_pasta # Adiciona o nome da pasta de origem\n",
+ " df_list.append(df)\n",
+ " except Exception as e:\n",
+ " print(f\"Erro ao ler o arquivo {arquivo}: {e}\")\n",
+ "\n",
+ "if df_list:\n",
+ " df_estoque = pd.concat(df_list, ignore_index=True)\n",
+ "\n",
+ "df_estoque['PDV'] = df_estoque['PDV'].astype(str)\n",
+ "\n",
+ "df_estoque['SKU_FINAL'] = np.where(df_estoque['SKU_PARA'] == \"-\", df_estoque['SKU'], df_estoque['SKU_PARA'])\n",
+ "\n",
+ "df_estoque['SKU_FINAL']=df_estoque['SKU_FINAL'].astype(str)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_tabela = pd.merge(left=df_tabela,right=df_estoque[['SKU','SKU_FINAL']],left_on='Código',right_on='SKU',how='inner')\n",
+ "\n",
+ "df_tabela['Código'] = df_tabela['SKU_FINAL']\n",
+ "\n",
+ "df_tabela = df_tabela.drop(columns=['SKU','SKU_FINAL'])\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_estoque = df_estoque.drop(columns=['DESCRICAO', 'CATEGORIA', 'CLASSE', 'FASES PRODUTO',\n",
+ " 'LANCAMENTO', 'DESATIVACAO','COBERTURA ALVO',\n",
+ " 'ESTOQUE DE SEGURANCA','COBERTURA PROJETADA', \n",
+ " 'Pasta_Origem'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_estoque"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_bi_preco = pd.read_excel(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C09\\arquivos pra gerar\\preços bi\\TABELA DE PREÇOS (4).xlsx\")\n",
+ "\n",
+ "df_bi_preco = df_bi_preco.drop(columns=['Descrição','Tipo Preço','CATEGORIA','LINHA','MARCA'])\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_tabela,right=df_pdv,on='MATCH',how='inner')\n",
+ "\n",
+ "df_final = df_final.drop_duplicates()\n",
+ "\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PDV'] = df_final['PDV'].astype('Int64')\n",
+ "df_final['Código'] = df_final['Código'].astype('Int64')\n",
+ "\n",
+ "\n",
+ "df_final = pd.merge(left=df_final,right=df_draft,left_on=['PDV','Código'],right_on=['PDV','SKU'],how='left')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final[(df_final['Código'] == 52023) & (df_final['PDV'] == 23712)]['Histórico de Vendas do Ciclo 202505']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns=['Sortimento P', 'Sortimento M',\n",
+ " 'Sortimento G','MARCA','SKU'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "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\")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#ignorando a PDV que ainda não está online\n",
+ "df_pdv = df_pdv[df_pdv['DESCRIÇÃO PDV'] != '23813-COMERCIO-HIB VALENTE']"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**ALTERAR NOME DA COLUNA \"ARQUIVO_ORIGEM\" PARA UMA DAS OPÇÕES ABAIXO:**\n",
+ "\n",
+ "*BOT.csv* \n",
+ "\n",
+ "*EUD.csv*\n",
+ "\n",
+ "*QDB.csv*"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "calendario['Date'] = pd.to_datetime(calendario['Date'])\n",
+ "\n",
+ "# Get today (normalized to midnight)\n",
+ "today = pd.Timestamp(\"today\").normalize()\n",
+ "\n",
+ "calendario['NUM_CICLO'] = calendario['Ciclo'].str[-2:].astype(int)\n",
+ "calendario['ANO_CICLO'] = calendario['Ciclo'].str[0:5]\n",
+ "\n",
+ "\n",
+ "calendario = calendario[calendario['MARCA'] == \"BOTICARIO\"]\n",
+ "\n",
+ "calendario = calendario.drop(columns='MARCA')\n",
+ "\n",
+ "calendario['CICLOMAIS2'] = calendario['ANO_CICLO'].astype(str) + (calendario['NUM_CICLO'].astype(int) + 2).astype(str).str.zfill(2) # >>>>>>>>> MUDAR PRA CICLO CORRETO \n",
+ "\n",
+ "ciclo_mais2 = calendario[calendario['Date'].dt.normalize() == today]['CICLOMAIS2'].iloc[0]\n",
+ "\n",
+ "\n",
+ "# Filter rows where date matches today\n",
+ "filtered_calendario = calendario[calendario['Ciclo'] == ciclo_mais2][:1]\n",
+ "\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['INICIO CICLO'].iloc[0] - today\n",
+ "\n",
+ "filtered_calendario['dias_ate_inicio'] = filtered_calendario['dias_ate_inicio'].dt.days.astype(int)\n",
+ "\n",
+ "filtered_calendario\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "filtered_calendario['MATCH'] = 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv['UF'] = np.where(df_pdv['UF'] == 'VDC','BA',df_pdv['UF'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#df_tabela = df_tabela[(df_tabela['Tipo de promoção'] == \"Revendedor\" ) | (df_tabela['Tipo de promoção'] == \"Promoções\") |(df_tabela['Tipo de promoção'] == \"Promoções | Revendedor\" )]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_draft['PDV'] = df_draft['PDV'].astype(str)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = pd.merge(left=df_final, right=filtered_calendario[['Ciclo','INICIO CICLO','FIM CICLO','DURAÇÃO','MATCH','dias_ate_inicio']], on='MATCH',how='inner')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['Código'] = df_final['Código'].astype('Int64') \n",
+ "df_final['PDV'] = df_final['PDV'].astype('Int64') \n",
+ "\n",
+ "df_estoque['PDV'] = df_estoque['PDV'].astype('Int64') \n",
+ "df_estoque['SKU_FINAL'] = df_estoque['SKU_FINAL'].astype('Int64') \n",
+ "\n",
+ "df_final = pd.merge(left=df_final,right=df_estoque,right_on=['PDV','SKU_FINAL'],left_on=['PDV','Código'],how='left')\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['Código'] = df_final['Código'].astype('str')\n",
+ "\n",
+ "df_bi_preco['SKU2'] = df_bi_preco['SKU2'].astype('str')\n",
+ "\n",
+ "df_final = pd.merge(left=df_final,right=df_bi_preco,right_on=['UF','SKU2'],left_on=['UF','Código'],how='left')\n",
+ "df_final.shape "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_bi_preco['SKU1'] = df_bi_preco['SKU1'].astype(str).str.replace('.0','',regex=False) \n",
+ "\n",
+ "df_final = pd.merge(left=df_final,right=df_bi_preco[['SKU1', 'SKU2', 'UF', 'PC', 'PV']],right_on=['UF','SKU1'],left_on=['UF','Código'],how='left')\n",
+ "df_final.shape "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['PRECO DE COMPRA'] = np.where(~df_final['PC_x'].isna(),df_final['PC_x'],df_final['PC_y'])\n",
+ "\n",
+ "df_final['PRECO DE VENDA'] = np.where(~df_final['PV_x'].isna(),df_final['PV_x'],df_final['PV_y'])\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['CANAL'] = np.where((df_final['CANAL'] == 'LJ') | (df_final['CANAL'] == 'HIB'), \"TODOS\" , np.where((df_final['CANAL'] == 'CD') | (df_final['CANAL'] == 'VD'), \"VD\", df_final['CANAL']))\n",
+ "\n",
+ "df_final['CANAL'].value_counts()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_estoque = df_estoque.rename(columns={'SKU_FINAL':'SKU_PARA_VALIDACAO'})\n",
+ "\n",
+ "df_estoque['SKU_PARA_VALIDACAO'] = df_estoque['SKU_PARA_VALIDACAO'].astype('Int64')\n",
+ "\n",
+ "df_final = pd.merge( left= df_final, right = df_estoque[['SKU_PARA_VALIDACAO','Arquivo_Origem']], left_on= 'SKU', right_on='SKU_PARA_VALIDACAO', how='left')\n",
+ "\n",
+ "df_final = df_final.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop(columns=['SKU1_x','SKU1_y','SKU2_x','SKU2_y','PC_x', 'PV_x','PC_y', 'PV_y','Subcategoria',\n",
+ "'Carteira Bloqueada Para Novos Pedidos',\n",
+ "'Quantidade por caixa'\n",
+ "])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['UFPRODUTO'] = df_final['UF'].astype(str) + df_final['SKU'].astype(str)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['Projeção Próximo Ciclo + 1'] =df_final['Projeção Próximo Ciclo + 1'] - df_final['Projeção Próximo Ciclo'] # projeção do ciclo em estudo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['Data Prevista Regularização'] = df_final['Data Prevista Regularização'].astype(str).replace('0','REGULAR')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.rename(columns={'Compra inteligente Próximo Ciclo + 1':'Compra inteligente Próximo Ciclo','Arquivo_Origem': 'MARCA'})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final[~df_final['Marca'].isna()]\n",
+ "df_final['Marca'].isna().sum()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.drop_duplicates()\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final[df_final.columns[26:43]] = df_final[df_final.columns[26:43]].fillna(0)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[26:44]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define list of target columns\n",
+ "sales_2024_cols = df_final.columns[26:44]\n",
+ "# Create a new column with the row-wise max\n",
+ "df_final['PICO DE VENDAS 2024'] = df_final[sales_2024_cols].max(axis=1)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[37:44]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "vendas_6_meses = df_final.columns[37:44]\n",
+ "\n",
+ "df_final['Pico Vendas Ultimos 6 ciclos'] = df_final[vendas_6_meses].max(axis=1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[37:44]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define as colunas mensais\n",
+ "colunas_mensais = df_final.columns[26:43]\n",
+ "\n",
+ "# Agrupa por PDV e calcula crescimento médio por PDV\n",
+ "def calcular_crescimento(grupo):\n",
+ " soma_mensal = grupo[colunas_mensais].sum() # soma por mês\n",
+ " variacao_mensal = soma_mensal.pct_change().dropna() # variação percentual mês a mês\n",
+ " variacao_mensal = variacao_mensal[np.isfinite(variacao_mensal)]\n",
+ "\n",
+ " if len(variacao_mensal) == 0:\n",
+ " return pd.Series({'CRESCIMENTO': np.nan})\n",
+ "\n",
+ " media = variacao_mensal.mean()\n",
+ " desvio = variacao_mensal.std()\n",
+ "\n",
+ " limite_sup = media + 2 * desvio\n",
+ " limite_inf = media - 2 * desvio\n",
+ "\n",
+ " variacoes_filtradas = variacao_mensal[variacao_mensal.between(limite_inf, limite_sup)]\n",
+ " crescimento = round(variacoes_filtradas.mean(), 4)\n",
+ " return pd.Series({'CRESCIMENTO': crescimento})\n",
+ "\n",
+ "# Aplica a função por PDV\n",
+ "crescimento_por_pdv = df_final.groupby('PDV').apply(calcular_crescimento)\n",
+ "\n",
+ "# Merge do resultado de volta no dataframe original\n",
+ "df_final = df_final.merge(crescimento_por_pdv, on='PDV', how='left')\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Suponha que os meses estão nas colunas 10 a 26 (17 colunas = 17 meses)\n",
+ "colunas_mensais = df_final.columns[26:43]\n",
+ "\n",
+ "# Passo 1: Soma todas as linhas (itens) por mês → resultado: total por mês\n",
+ "soma_mensal = df_final[colunas_mensais].sum()\n",
+ "\n",
+ "# Passo 2: Calcula a variação percentual de um mês para o outro\n",
+ "variacao_mensal = soma_mensal.pct_change()\n",
+ "variacao_mensal = variacao_mensal.dropna()\n",
+ "\n",
+ "variacao_mensal = variacao_mensal[np.isfinite(variacao_mensal)]\n",
+ "\n",
+ "# Passo 3: Calcula a média da variação (ignorando o primeiro NaN)\n",
+ "media_variacao = variacao_mensal[1:].mean()\n",
+ "\n",
+ "# Calcula média e desvio padrão\n",
+ "media = variacao_mensal.mean()\n",
+ "desvio = variacao_mensal.std()\n",
+ "\n",
+ "# Define limite (ex: 2 desvios padrão)\n",
+ "limite_superior = media + 2 * desvio\n",
+ "limite_inferior = media - 2 * desvio\n",
+ "\n",
+ "# Filtra dados dentro do limite\n",
+ "filtro = variacao_mensal.between(limite_inferior, limite_superior)\n",
+ "df_filtrado = variacao_mensal[filtro]\n",
+ "CRESCIMENTO = round(df_filtrado.mean(),4)\n",
+ "\n",
+ "df_final['CRESCIMENTO_GERAL'] = CRESCIMENTO\n",
+ "\n",
+ "CRESCIMENTO\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "vendas_todos_historicos = df_final.columns[26:44]\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = df_final[vendas_todos_historicos].median(axis=1)\n",
+ "\n",
+ "df_final['MEDIA DO HISTÓRICO'] = df_final[vendas_todos_historicos].mean(axis=1)\n",
+ "\n",
+ "medi = df_final.groupby(['CANAL'])['MEDIANA DO HISTÓRICO'].max().reset_index()\n",
+ "medi = medi.rename(columns={'MEDIANA DO HISTÓRICO':'med_por_canal'})\n",
+ "\n",
+ "df_final = pd.merge(left=df_final, right=medi,on='CANAL',how='inner')\n",
+ "\n",
+ "medi"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[28:29].str.split(\" \")[0][-1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mesmo_ciclo_ano_passado = df_final.columns[28:29]\n",
+ "ciclo_ano_passado = df_final.columns[28:29].str.split(\" \")[0][-1]\n",
+ "df_final[ciclo_ano_passado] = df_final[mesmo_ciclo_ano_passado]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final['CRESCIMENTO_FINAL'] = df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'] #crescimento do pdv\n",
+ "\n",
+ "df_final['CRESCIMENTO_FINAL'] = np.where(df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO']>0.8,0.8,df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'])\n",
+ "\n",
+ "df_final['CRESCIMENTO_FINAL'] = np.where(df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO']<0,0,df_final['CRESCIMENTO_GERAL'] + df_final['CRESCIMENTO'])\n",
+ "\n",
+ "df_final['MEDIANA DO HISTÓRICO'] = np.where(df_final['MEDIANA DO HISTÓRICO']==0,df_final['MEDIA DO HISTÓRICO'] ,df_final['MEDIANA DO HISTÓRICO'])\n",
+ "\n",
+ "# Primeiro cálculo intermediário\n",
+ "df_final['PV GINSENG'] = np.where(df_final['CRESCIMENTO_FINAL'] * df_final[ciclo_ano_passado] + df_final[ciclo_ano_passado] <1,\n",
+ " round(df_final['CRESCIMENTO_FINAL'] * df_final['MEDIANA DO HISTÓRICO']+ df_final['MEDIANA DO HISTÓRICO'],0), \n",
+ " round(df_final['CRESCIMENTO_FINAL']*df_final[ciclo_ano_passado]+df_final[ciclo_ano_passado],0))\n",
+ "\n",
+ "\n",
+ "df_final.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.columns[26:39]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final = df_final.rename(columns={df_final.columns[39]: \"C-4\", df_final.columns[40]: \"C-3\",df_final.columns[41]: \"C-2\",df_final.columns[42]: \"C-1\"})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final.drop(columns=df_final.columns[26:39], inplace=True)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# List all columns except the two\n",
+ "cols_to_group_by = df_final.columns.difference(['DDV PREVISTO', 'COBERTURA ATUAL'])\n",
+ "\n",
+ "# Group and aggregate\n",
+ "df_final_dedup = (\n",
+ " df_final\n",
+ " .groupby(list(cols_to_group_by), dropna=False)[['DDV PREVISTO', 'COBERTURA ATUAL']]\n",
+ " .max()\n",
+ " .reset_index()\n",
+ ")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['DDV PREVISTO'] = df_final_dedup['DDV PREVISTO'].fillna(0.01)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup[(df_final['PDV'] == 23712)]['C-3']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['DDV PREVISTO'] = np.where(\n",
+ " df_final_dedup['DDV PREVISTO'] == 0,\n",
+ " 0.01,\n",
+ " df_final_dedup['DDV PREVISTO']\n",
+ ")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['EST PROJE FINAL CICLO ATUAL'] = (df_final_dedup['Estoque Atual'] + df_final_dedup['Estoque em Transito']) - round(df_final_dedup['dias_ate_inicio'] * df_final_dedup['DDV PREVISTO'],0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['EST PROJE FINAL CICLO ATUAL'] = np.where(df_final_dedup['EST PROJE FINAL CICLO ATUAL']<0,0,df_final_dedup['EST PROJE FINAL CICLO ATUAL'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['VENDAS R$ PV GINSENG'] = df_final_dedup['PRECO DE VENDA'] * df_final_dedup['PV GINSENG']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_estoque.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Columns to bring up front\n",
+ "priority_cols = [\n",
+ " 'SKU',\t'Marca',\t'INICIO CICLO',\n",
+ " 'FIM CICLO',\t'DURAÇÃO',\t'PRECO DE COMPRA',\t'PRECO DE VENDA',\n",
+ " 'UFPRODUTO',\t'Item Desativado',\t'Data Prevista Regularização',\n",
+ " 'ANALISTA',\t'UF',\t'CANAL',\t'PDV',\t'DESCRIÇÃO PDV',\t'Classe',\n",
+ " 'Descrição',\t'Categoria',\n",
+ "\t'ESTOQUE ATUAL', 'ESTOQUE EM TRANSITO',\t'COBERTURA ATUAL',\n",
+ " 'Pedido Pendente',\t'PICO DE VENDAS 2024','Pico Vendas Ultimos 6 ciclos',\n",
+ " 'C-4',\t'C-3',\t'C-2',\t'C-1',\t'Histórico de Vendas do Ciclo Atual',\n",
+ " 'Dias sem venda'\n",
+ "]\n",
+ "\n",
+ "# All remaining columns\n",
+ "other_cols = [col for col in df_final_dedup.columns if col not in priority_cols]\n",
+ "\n",
+ "# Reorder\n",
+ "'Ação consumidor', 'Percentual de desconto consumidor', 'Ação revendedor', 'Percentual de desconto revendedor', '202408'\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#df_final_dedup = df_final_dedup[priority_cols + other_cols]\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['RBV 202406'] = df_final_dedup['PRECO DE VENDA'] * df_final_dedup[ciclo_ano_passado] "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['COB PROJETADA'] = np.where(\n",
+ " df_final_dedup['DDV PREVISTO'] != 0,\n",
+ " (df_final_dedup['EST PROJE FINAL CICLO ATUAL'] + df_final_dedup['PV GINSENG']) / df_final_dedup['DDV PREVISTO'],\n",
+ " 999)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup.drop(df_final_dedup.columns[39:40], axis=1, inplace=True)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup.drop(columns=['dias_ate_inicio','SKU_FINAL',\n",
+ " 'CRESCIMENTO'],inplace=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#df_final_dedup['MARCA'] = df_final_dedup['MARCA'].str.replace('.csv','',regex=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['COB PROJETADA'] = df_final_dedup['COB PROJETADA'].fillna(999)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "marca_promo = df_estoque['Arquivo_Origem'].iloc[0].replace('.csv','')\n",
+ "marca_promo"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['INICIO CICLO'] = pd.to_datetime(df_final_dedup['INICIO CICLO'], dayfirst=True).dt.strftime('%d/%m/%Y')\n",
+ "\n",
+ "df_final_dedup['FIM CICLO'] = pd.to_datetime(df_final_dedup['FIM CICLO'], dayfirst=True).dt.strftime('%d/%m/%Y')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['Percentual de desconto revendedor'] = np.where((df_final_dedup['Percentual de desconto revendedor'].isna()) & (~df_final_dedup['Percentual de desconto consumidor'].isna()),df_final_dedup['Percentual de desconto consumidor'],df_final_dedup['Percentual de desconto revendedor'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_pdv_origi['PDV'] = df_pdv_origi['PDV'].astype('Int64')\n",
+ "df_final_dedup['PDV'] = df_final_dedup['PDV'].astype('Int64')\n",
+ "\n",
+ "\n",
+ "df_final_dedup = pd.merge(left=df_final_dedup,right=df_pdv_origi[['PDV','CANAL','UF']],how='inner',on='PDV')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_vdc = pd.read_csv(r\"C:\\Users\\joao.herculano\\GRUPO GINSENG\\Assistência Suprimentos - 2025\\SUPRIMENTOS\\DB_PROMOÇÕES\\BOTICARIO\\C10\\VENDA VITORIA 2024\\VENDA VITORIA.csv\")\n",
+ "\n",
+ "df_vdc['PRODUTO'] = df_vdc['PRODUTO'].astype('Int64')\n",
+ "\n",
+ "df_final_dedup['Código'] = df_final_dedup['Código'].astype('Int64')\n",
+ "\n",
+ "df_final_dedup =pd.merge(left=df_final_dedup,right=df_vdc[['PDV GINSENG','PRODUTO',ciclo_ano_passado]],left_on= ['PDV','Código'],right_on= ['PDV GINSENG','PRODUTO'],how='left' )\n",
+ "\n",
+ "df_final_dedup['202410_y'] = df_final_dedup['202410_y'].fillna(0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['202410_x'] = np.where(df_final_dedup['202410_y']>0,df_final_dedup['202410_y'],df_final_dedup['202410_x'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup = df_final_dedup.drop(columns=['Arquivo_Origem_x','Arquivo_Origem_y','CANAL_x','Canal',\n",
+ " 'Ciclo_x','Ciclo_y','DURAÇÃO','FIM CICLO','Foco','INICIO CICLO','MATCH',\n",
+ " 'PRECO DE COMPRA','SKU','SKU_PARA','SKU_PARA_VALIDACAO',\n",
+ " 'Tipo de pedido',\t'Tipo de produto','UFPRODUTO','Unidade de negócio','EST PROJE FINAL CICLO ATUAL',\n",
+ " 'UF_x','RBV 202406','Região','Catálogo','SKU','VENDAS R$ PV GINSENG','Data Prevista Regularização',\n",
+ " 'IAF', 'Item Desativado','Tipo de promoção','PDV GINSENG','PRODUTO','202410_y',\n",
+ " 'ESTOQUE ATUAL', 'ESTOQUE EM TRANSITO','COBERTURA ATUAL + TRANSITO',\n",
+ " 'DDV PREVISTO',\t'COB PROJETADA','COBERTURA ATUAL',\n",
+ " 'CRESCIMENTO_FINAL',\t'CRESCIMENTO_GERAL','med_por_canal','PEDIDO PENDENTE'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup = df_final_dedup.rename(columns={'CANAL_y':'CANAL','UF_y':'UF','Marca':'LINHA','202410_x':'202410'})\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup.columns"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup[(df_final['PDV'] == 23712)]['C-3']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "priority_cols = ['SUPERVISOR','ANALISTA','UF','CANAL','LINHA','PDV','DESCRIÇÃO PDV','Código','Descrição','Categoria', \n",
+ "'Classe','Percentual de desconto consumidor','Ação consumidor','Percentual de desconto revendedor','Ação revendedor','C-1',\n",
+ "'C-2', 'C-3', 'C-4','Histórico de Vendas do Ciclo Atual','Estoque Atual','Estoque em Transito','Pedido Pendente',\n",
+ "'Projeção Próximo Ciclo + 1','Projeção Próximo Ciclo']\n",
+ "\n",
+ "# All remaining columns\n",
+ "other_cols = [col for col in df_final_dedup.columns if col not in priority_cols]\n",
+ "\n",
+ "# Reorder\n",
+ "df_final_dedup = df_final_dedup[priority_cols + other_cols]\n",
+ "\n",
+ "df_final_dedup['SUGESTÃO ABASTECIMENTO'] = ''\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup = df_final_dedup.drop_duplicates()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup['PV GINSENG'].sum()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df_final_dedup[df_final_dedup['PDV'] == 23712]['PICO DE VENDAS 2024'].isna().sum()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Export to Excel\n",
+ "output_file = f'C:\\\\Users\\\\joao.herculano\\\\Documents\\\\promoção_{marca_promo}_{ciclo_mais2}28.05.xlsx'\n",
+ "with pd.ExcelWriter(output_file, engine='openpyxl') as writer:\n",
+ " df_final_dedup.to_excel(writer, index=False)\n",
+ "\n",
+ "# Apply styles\n",
+ "wb = load_workbook(output_file)\n",
+ "ws = wb['Sheet1']\n",
+ "\n",
+ "# Style header\n",
+ "header_fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid') # Light Blue\n",
+ "header_font = Font(color='FFFFFF', bold=True) # White & Bold\n",
+ "\n",
+ "for cell in ws[1]:\n",
+ " cell.fill = header_fill\n",
+ " cell.font = header_font\n",
+ "\n",
+ "# Style rows: gray/white alternating\n",
+ "gray_fill = PatternFill(start_color='DDDDDD', end_color='DDDDDD', fill_type='solid') # Light gray\n",
+ "\n",
+ "for i, row in enumerate(ws.iter_rows(min_row=2, max_row=ws.max_row), start=2):\n",
+ " if i % 2 == 0:\n",
+ " for cell in row:\n",
+ " cell.fill = gray_fill\n",
+ "\n",
+ "# Save styled workbook\n",
+ "wb.save(output_file)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "coisas q saem\n",
+ "\n",
+ "Lançamento\n",
+ "Subcategoria\n",
+ "Projeção Próximo Ciclo \n",
+ "Promoção Próximo Ciclo\n",
+ "Compra inteligente semanal/Sugestão de compra\n",
+ "Compra inteligente Próximo Ciclo\n",
+ "Planograma\n",
+ "Carteira Bloqueada Para Novos Pedidos\n",
+ "Quantidade por caixa\n",
+ "Preço Sell In\n",
+ "Quantidade\n",
+ "Item analisado\n",
+ "Tipo Preço\n",
+ "\n",
+ ">>>>>>>>>>>>>NAO ESTÁ PEGANDO O MERGE COM O DF_ESTOQUE\n",
+ "\n",
+ "\n",
+ "CRIAR PROJEÇÃO DE VENDA DO CICLO ATUAL\n",
+ "Compra inteligente Próximo Ciclo + 1 >>>>>>> RENAME PRA NOROMAL"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/relatório_improdutivo/teste email excel.py b/relatório_improdutivo/teste email excel.py
new file mode 100644
index 0000000..7a8ef0c
--- /dev/null
+++ b/relatório_improdutivo/teste email excel.py
@@ -0,0 +1,241 @@
+# enviar_email_excel.py
+
+import smtplib
+import ssl
+import psycopg2
+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 = psycopg2.connect(
+ host=config['banco']['host'], # ou IP do servidor
+ port="5432", # padrão do PostgreSQL
+ database="ginseng",
+ user=config['banco']['user'],
+ password= config['banco']['password']
+)
+
+# Criar um cursor para executar comandos SQL
+cur = conn.cursor()
+
+
+# 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 "public"."draft"
+where dayswithoutsales > 40
+and deactivation = ''
+and stock_actual > 0
+and isproductdeactivated is not null
+and currentcyclesales = 0
+'''
+df = pd.read_sql(query, conn)
+
+cur.close()
+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"""
+
+
+ {boa}
+
+
+ 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).
+
+
+
+ Este relatório contempla exclusivamente os itens que possuem saldo em estoque,
+ mas que estão sem vendas há mais de 40 dias.
+
+
+
+ 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.
+
+
+
+ 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.
+
+
+
+ Para mais informações, favor consultar a planilha em anexo.
+
+
+ Segue resumo:
+
+
+
+"""
+
+
+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.")
diff --git a/relatório_ruptura/ruptura projetada 23.05.py b/relatório_ruptura/ruptura projetada 23.05.py
new file mode 100644
index 0000000..a7be12b
--- /dev/null
+++ b/relatório_ruptura/ruptura projetada 23.05.py
@@ -0,0 +1,221 @@
+import smtplib
+import ssl
+import psycopg2
+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 = psycopg2.connect(
+ host=config['banco']['host'],
+ port="5432",
+ database="ginseng",
+ user=config['banco']['user'],
+ password=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 = '' 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 = false THEN 'REGULAR' ELSE 'CRITICO' END AS status_item
+FROM Draft
+WHERE isproductdeactivated = false 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"""
+
+
+ {boa}
+ Compartilhamos o relatório de ruptura projetada, com o objetivo de monitorar os itens com maior risco de ruptura nos próximos dias.
+ O relatório a seguir apresenta as seguintes informações:
+
+ - Estoque atual;
+ - Trânsito;
+ - Ruptura projetada;
+ - Maior excesso por estado, assim como o PDV que o possui.
+
+ 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.
+ Importante: O relatório está em processo de desenvolvimento e pode sofrer mudanças futuras no layout. Ficamos à disposição para esclarecer quaisquer dúvidas.
+
+
+
+
+
+"""
+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.")
\ No newline at end of file