daily_zones_exporter.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import mysql.connector
  2. import pandas as pd
  3. from openpyxl import Workbook
  4. from openpyxl.utils.dataframe import dataframe_to_rows
  5. from openpyxl.utils import get_column_letter
  6. from openpyxl.styles import Alignment
  7. def create_row(row_data, document_date='', shop_name=''):
  8. new_row_temp = {
  9. '单据日期': document_date,
  10. '区域': '',
  11. '组长': row_data['组长'],
  12. '999及以内单数': row_data['999及以内单数'].sum(),
  13. '1000-1999单数': row_data['1000-1999单数'].sum(),
  14. '2000-2899单数': row_data['2000-2899单数'].sum(),
  15. '2900-3999单数': row_data['2900-3999单数'].sum(),
  16. '4000-4999单数': row_data['4000-4999单数'].sum(),
  17. '5000-7999单数': row_data['5000-7999单数'].sum(),
  18. '8000以上单数': row_data['8000以上单数'].sum(),
  19. '满足奖励条件大单数': row_data['满足奖励条件大单数'].sum(),
  20. '最大单件数': row_data['最大单件数'].max(),
  21. '最大单金额': row_data['最大单金额'].max(),
  22. '总单数': row_data['总单数'].sum(),
  23. '2000+单数': row_data['2000+单数'].sum(),
  24. '2000+单数占比': row_data['2000+单数'].sum() / row_data['总单数'].sum() if row_data['总单数'].sum() != 0 else 0
  25. }
  26. if shop_name == '总计':
  27. new_row_temp['组长'] = '总计'
  28. else:
  29. new_row_temp['组长'] = f'{shop_name}汇总'
  30. return new_row_temp
  31. class DailyZonesSalesExporter:
  32. def __init__(self, db_config):
  33. self.db_config = db_config
  34. self.query = """
  35. SELECT *, IF(temp.`总单数` = 0, 0, temp.`2000+单数` / temp.`总单数`) AS `2000+单数占比`
  36. FROM (
  37. SELECT
  38. t1.document_date AS `单据日期`,
  39. t1.channel_type AS `店铺类型`,
  40. t3.division AS `区域`,
  41. t3.group_leader AS `组长`,
  42. SUM(CASE WHEN t1.remaining_amount_after_return <= 999 THEN 1 ELSE 0 END) AS `999及以内单数`,
  43. SUM(CASE WHEN t1.remaining_amount_after_return > 999 AND t1.remaining_amount_after_return < 2000 THEN 1 ELSE 0 END) AS `1000-1999单数`,
  44. SUM(CASE WHEN t1.remaining_amount_after_return >= 2000 AND t1.remaining_amount_after_return <= 2899 THEN 1 ELSE 0 END) AS `2000-2899单数`,
  45. SUM(CASE WHEN t1.remaining_amount_after_return > 2899 AND t1.remaining_amount_after_return <= 3999 THEN 1 ELSE 0 END) AS `2900-3999单数`,
  46. SUM(CASE WHEN t1.remaining_amount_after_return > 3999 AND t1.remaining_amount_after_return <= 4999 THEN 1 ELSE 0 END) AS `4000-4999单数`,
  47. SUM(CASE WHEN t1.remaining_amount_after_return > 4999 AND t1.remaining_amount_after_return <= 7999 THEN 1 ELSE 0 END) AS `5000-7999单数`,
  48. SUM(CASE WHEN t1.remaining_amount_after_return > 7999 THEN 1 ELSE 0 END) AS `8000以上单数`,
  49. SUM(CASE WHEN t2.refund_meets_big_order != '不符合' THEN 1 ELSE 0 END) AS `满足奖励条件大单数`,
  50. MAX(t2.big_order_items) AS `最大单件数`,
  51. MAX(t2.big_order_amount) AS `最大单金额`,
  52. COUNT(1) AS `总单数`,
  53. SUM(CASE WHEN t1.remaining_amount_after_return >= 2000 THEN 1 ELSE 0 END) AS `2000+单数`
  54. FROM sd_sales_order AS t1
  55. LEFT JOIN sd_big_sales_order AS t2 ON t1.sale_id = t2.sale_id
  56. LEFT JOIN sd_store_info AS t3 ON t3.f360_code = t1.store_code
  57. WHERE t3.group_leader IS NOT NULL
  58. GROUP BY t1.document_date, t1.channel_type, t3.group_leader, t3.division
  59. ORDER BY `单据日期`, `店铺类型`
  60. ) AS temp;
  61. """
  62. def export(self, output_file='output_by_shop_type_with_summary_and_total.xlsx'):
  63. global cursor, connection
  64. try:
  65. connection = mysql.connector.connect(**self.db_config)
  66. cursor = connection.cursor()
  67. cursor.execute(self.query)
  68. rows = cursor.fetchall()
  69. columns = [desc[0] for desc in cursor.description]
  70. finally:
  71. cursor.close()
  72. connection.close()
  73. df = pd.DataFrame(rows, columns=columns)
  74. for col in df.columns:
  75. if col not in ['单据日期', '店铺类型', '区域', '组长']:
  76. df[col] = pd.to_numeric(df[col], errors='coerce')
  77. grouped_by_date = df.groupby('单据日期', sort=False)
  78. new_data = []
  79. for date, date_df in grouped_by_date:
  80. grouped_by_shop = date_df.groupby('店铺类型', sort=False)
  81. for shop, shop_df in grouped_by_shop:
  82. for _, row in shop_df.iterrows():
  83. new_row = {
  84. '单据日期': date,
  85. '区域': row['区域'],
  86. '组长': row['组长'],
  87. '999及以内单数': row['999及以内单数'],
  88. '1000-1999单数': row['1000-1999单数'],
  89. '2000-2899单数': row['2000-2899单数'],
  90. '2900-3999单数': row['2900-3999单数'],
  91. '4000-4999单数': row['4000-4999单数'],
  92. '5000-7999单数': row['5000-7999单数'],
  93. '8000以上单数': row['8000以上单数'],
  94. '满足奖励条件大单数': row['满足奖励条件大单数'],
  95. '最大单件数': row['最大单件数'],
  96. '最大单金额': row['最大单金额'],
  97. '总单数': row['总单数'],
  98. '2000+单数': row['2000+单数'],
  99. '2000+单数占比': row['2000+单数占比'],
  100. }
  101. new_data.append(new_row)
  102. summary_row = create_row(shop_df, date, shop)
  103. new_data.append(summary_row)
  104. day_summary_row = create_row(date_df, date, '总计')
  105. new_data.append(day_summary_row)
  106. final_df = pd.DataFrame(new_data)
  107. wb = Workbook()
  108. ws = wb.active
  109. for r in dataframe_to_rows(final_df, index=False, header=True):
  110. ws.append(r)
  111. current_date = None
  112. start_row = None
  113. for row in range(2, ws.max_row + 1):
  114. date_cell = ws.cell(row=row, column=1)
  115. date_value = date_cell.value
  116. if date_value != current_date:
  117. if start_row is not None and row > start_row + 1:
  118. ws.merge_cells(start_row=start_row, start_column=1, end_row=row - 1, end_column=1)
  119. merged_cell = ws.cell(row=start_row, column=1)
  120. merged_cell.alignment = Alignment(horizontal='center', vertical='center')
  121. current_date = date_value
  122. start_row = row
  123. if start_row is not None and start_row < ws.max_row:
  124. ws.merge_cells(start_row=start_row, start_column=1, end_row=ws.max_row, end_column=1)
  125. merged_cell = ws.cell(row=start_row, column=1)
  126. merged_cell.alignment = Alignment(horizontal='center', vertical='center')
  127. header = {cell.value: cell.column for cell in ws[1]}
  128. numeric_columns = [
  129. '999及以内单数', '1000-1999单数', '2000-2899单数', '2900-3999单数',
  130. '4000-4999单数', '5000-7999单数', '8000以上单数', '满足奖励条件大单数',
  131. '最大单件数', '最大单金额', '总单数', '2000+单数'
  132. ]
  133. for col_name in numeric_columns:
  134. if col_name in header:
  135. col_letter = get_column_letter(header[col_name])
  136. for cell in ws[col_letter]:
  137. if cell.row == 1:
  138. continue
  139. cell.number_format = '0'
  140. if '2000+单数占比' in header:
  141. col_letter = get_column_letter(header['2000+单数占比'])
  142. for cell in ws[col_letter]:
  143. if cell.row == 1:
  144. continue
  145. cell.number_format = '0.00%'
  146. wb.save(output_file)
  147. print(f"✅ 数据已成功导出到 {output_file}")
  148. def export_to_dataframe(self):
  149. connection = mysql.connector.connect(**self.db_config)
  150. cursor = connection.cursor()
  151. try:
  152. cursor.execute(self.query)
  153. rows = cursor.fetchall()
  154. columns = [desc[0] for desc in cursor.description]
  155. finally:
  156. cursor.close()
  157. connection.close()
  158. df = pd.DataFrame(rows, columns=columns)
  159. for col in df.columns:
  160. if col not in ['单据日期', '店铺类型', '区域', '组长']:
  161. df[col] = pd.to_numeric(df[col], errors='coerce')
  162. grouped_by_date = df.groupby('单据日期', sort=False)
  163. new_data = []
  164. for date, date_df in grouped_by_date:
  165. grouped_by_shop = date_df.groupby('店铺类型', sort=False)
  166. for shop, shop_df in grouped_by_shop:
  167. for _, row in shop_df.iterrows():
  168. new_row = {
  169. '单据日期': date,
  170. '区域': row['区域'],
  171. '组长': row['组长'],
  172. '999及以内单数': row['999及以内单数'],
  173. '1000-1999单数': row['1000-1999单数'],
  174. '2000-2899单数': row['2000-2899单数'],
  175. '2900-3999单数': row['2900-3999单数'],
  176. '4000-4999单数': row['4000-4999单数'],
  177. '5000-7999单数': row['5000-7999单数'],
  178. '8000以上单数': row['8000以上单数'],
  179. '满足奖励条件大单数': row['满足奖励条件大单数'],
  180. '最大单件数': row['最大单件数'],
  181. '最大单金额': row['最大单金额'],
  182. '总单数': row['总单数'],
  183. '2000+单数': row['2000+单数'],
  184. '2000+单数占比': row['2000+单数占比'],
  185. }
  186. new_data.append(new_row)
  187. summary_row = create_row(shop_df, date, shop)
  188. new_data.append(summary_row)
  189. day_summary_row = create_row(date_df, date, '总计')
  190. new_data.append(day_summary_row)
  191. final_df = pd.DataFrame(new_data)
  192. return final_df