From b2fb8fd8e31ea8f7bc744d13b358fe9f37179551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Herculano?= Date: Thu, 29 May 2025 09:48:20 -0300 Subject: [PATCH] innit 29.05 --- Lançamentos/Script_lançamento_EUD_v2.ipynb | 1481 ++++++++++++++++ Lançamentos/Script_lançamento_boti_v2.ipynb | 785 +++++++++ Ruptura_Projetada/ruptura projetada 23.05.py | 221 +++ promoção/promoção_EUD_ciclo07.ipynb | 1644 ++++++++++++++++++ promoção/promoção_boti_ciclo07.ipynb | 1224 +++++++++++++ relatório_improdutivo/teste email excel.py | 241 +++ relatório_ruptura/ruptura projetada 23.05.py | 221 +++ 7 files changed, 5817 insertions(+) create mode 100644 Lançamentos/Script_lançamento_EUD_v2.ipynb create mode 100644 Lançamentos/Script_lançamento_boti_v2.ipynb create mode 100644 Ruptura_Projetada/ruptura projetada 23.05.py create mode 100644 promoção/promoção_EUD_ciclo07.ipynb create mode 100644 promoção/promoção_boti_ciclo07.ipynb create mode 100644 relatório_improdutivo/teste email excel.py create mode 100644 relatório_ruptura/ruptura projetada 23.05.py 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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CicloINICIO CICLOFIM CICLODURAÇÃOMARCADateNUM_CICLOANO_CICLOCICLOMAIS2dias_ate_iniciomatch
2262C2025112025-07-162025-08-0521EUDORA2025-07-1611C2025C202514541
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PDVCódigo do ProdutoCicloQuantidade Acumulada
02096850112C20231044
12096850112C20231160
22096850112C20231370
32096850112C20231473
42096850112C20231575
...............
5282254152699C20240910
5292254152699C20241011
5302254152699C20241113
5312254156572C20241138
5322254157390C20241122
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CANALmed_por_canal
0CD17.0
1HIB1.0
2VD12.0
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PDVDEPARA.PracticoCódigoCiclo vdcQuantidade Acumulada vdc
02370148617C20230124
12370150165C20231212
22370150165C20241114
32370150165C20241316
42370150224C2024032
\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:

+ +

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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
CicloINICIO CICLOFIM CICLODURAÇÃOMARCADateNUM_CICLOANO_CICLOCICLOMAIS2dias_ate_inicio
2220C2025102025-06-252025-07-1521EUDORA2025-06-2510C2025C20251228
\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:

+ +

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