gchr.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import json
  2. import logging
  3. import os
  4. from datetime import datetime
  5. from pathlib import Path
  6. import pandas as pd
  7. from gchr.gchr_bookings import GchrBookings
  8. from gchr.gchr_export import GchrExportFormat, get_export_fn
  9. from gchr.gchr_model import ACCOUNT_INFO, GchrConfig, GchrExportConfig
  10. from gchr.gchr_translate import load_translation
  11. class GCHR:
  12. cfg: GchrConfig
  13. booking_date: datetime
  14. bookings: GchrBookings
  15. _df_translate: pd.DataFrame = None
  16. df_translate2: pd.DataFrame = None
  17. makes: dict[str, str] = None
  18. sites: dict[str, str] = None
  19. current_year: str
  20. current_month: str
  21. timestamp: str
  22. def __init__(self, base_dir: str) -> None:
  23. self.base_dir = base_dir
  24. cfg_file = self.base_dir + "\\config\\gchr2.json"
  25. if Path(cfg_file).exists():
  26. with open(cfg_file, "r") as frh:
  27. self.cfg = GchrConfig(**json.load(frh))
  28. else:
  29. self.cfg = GchrConfig()
  30. os.makedirs(self.base_dir + "/data", exist_ok=True)
  31. os.makedirs(self.base_dir + "/export/temp", exist_ok=True)
  32. os.makedirs(self.base_dir + "/logs", exist_ok=True)
  33. self.account_translation = f"{self.base_dir}/data/Kontenrahmen_uebersetzt.csv"
  34. self.first_month_of_financial_year = "10"
  35. self.bookings = GchrBookings(self.base_dir, self.first_month_of_financial_year)
  36. self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  37. pd.set_option("display.max_rows", 500)
  38. pd.set_option("display.float_format", lambda x: "%.2f" % x)
  39. @property
  40. def debug_file(self) -> str:
  41. return f"{self.logs_dir}/debug_{self.timestamp}.csv"
  42. @property
  43. def account_ignored(self) -> str:
  44. return f"{self.export_info_dir}/ignoriert_{self.period}.csv"
  45. # self.account_invalid = f"{self.export_info_dir}/ungueltig_{self.period}.csv"
  46. def export_all_periods(self, overwrite=False, today=None) -> None:
  47. periods = GCHR.get_all_periods(today)
  48. for year, month in periods:
  49. filename = self.export_filename_for_period(year, month)
  50. if overwrite or not Path(filename).exists():
  51. os.makedirs(Path(filename).parent.joinpath("info"), exist_ok=True)
  52. self.export_period(year, month)
  53. @staticmethod
  54. def get_all_periods(today=None) -> list[tuple[str, str]]:
  55. dt = datetime.now()
  56. if today is not None:
  57. dt = datetime.fromisoformat(today)
  58. prev = str(dt.year - 1)
  59. periods = [(prev, str(x).zfill(2)) for x in range(dt.month, 13)] + [
  60. (str(dt.year), str(x).zfill(2)) for x in range(1, dt.month)
  61. ]
  62. return periods
  63. def export_period(self, year: str, month: str) -> str:
  64. export_fn = get_export_fn(GchrExportFormat.SKR51)
  65. # Kontensalden laden
  66. df_bookings = self.bookings.filter_bookings(year, month)
  67. all_periods = set(df_bookings["Bookkeep Period"].to_list())
  68. bookkeep_period_date = datetime(int(year), int(month), 28)
  69. if df_bookings.shape[0] == 0 or len(all_periods) <= 1 or self.booking_date < bookkeep_period_date:
  70. logging.error("ABBRUCH!!! Keine Daten vorhanden!")
  71. return False
  72. filter_to = year + month
  73. period_no = list(self.bookings.bookkeep_filter.keys()).index(filter_to) + 1
  74. logging.info("df_bookings: " + str(df_bookings.shape))
  75. # Join auf Übersetzung
  76. df_combined = df_bookings.merge(self._df_translate, how="inner", on="Konto_Nr_Händler")
  77. logging.info(f"df_combined: {df_combined.shape}")
  78. df_pivot = df_combined.pivot_table(
  79. index=["Konto_Nr_SKR51"],
  80. columns=["period"],
  81. values="amount",
  82. aggfunc="sum",
  83. margins=True,
  84. margins_name="CumulatedYear",
  85. )
  86. df_pivot.drop(index="CumulatedYear", inplace=True)
  87. logging.info("df_pivot: " + str(df_pivot.shape))
  88. df = df_pivot.merge(self.df_translate2, how="inner", on="Konto_Nr_SKR51")
  89. makes_used = {}
  90. for m in sorted(list(set(df["Marke"].to_list()))):
  91. if m not in self.makes:
  92. continue
  93. makes_used[m] = self.makes[m]
  94. sites_used = {}
  95. for s in sorted(list(set((df["Marke"] + "-" + df["Standort"]).to_list()))):
  96. if s not in self.sites:
  97. continue
  98. sites_used[s] = self.sites[s]
  99. from_label = ["Marke", "Standort", "Konto_Nr", "Kostenstelle", "Absatzkanal", "Kostenträger", "KRM"]
  100. to_label = ["Make", "Site", "Account", "Origin", "SalesChannel", "CostCarrier", "CostAccountingString"]
  101. col_dict = dict(zip(from_label, to_label))
  102. df = df.rename(columns=col_dict)
  103. export_filename = self.export_filename_for_period(year, month)
  104. export_csv = export_filename[:-4] + ".csv"
  105. df.to_csv(export_csv, decimal=",", sep=";", encoding="latin-1", index=False)
  106. df = df[df["IsNumeric"] != False].groupby(ACCOUNT_INFO, as_index=False).aggregate("sum")
  107. # Infos ergänzen
  108. df["Decimals"] = 2
  109. # df.sort_values(by=["Konto_Nr_SKR51"], inplace=True)
  110. logging.info(df.shape)
  111. main_sites = [self.sites[s] for s in sites_used if s in self.sites and self.sites[s] != "0000"]
  112. for i, main_site in enumerate(main_sites):
  113. filename = export_filename
  114. if i > 0:
  115. filename = f"{filename[:-4]}_{main_site}.xml"
  116. export_cfg = GchrExportConfig(
  117. main_site,
  118. year,
  119. month,
  120. makes_used,
  121. sites_used,
  122. self.first_month_of_financial_year,
  123. period_no,
  124. self.bookings.bookkeep_filter,
  125. filename,
  126. df.to_dict(orient="records"),
  127. )
  128. export_fn(export_cfg)
  129. # Join auf Übersetzung - nicht zugeordnet
  130. df_ignored = df_bookings.merge(self.df_translate, how="left", on="Konto_Nr_Händler")
  131. df_ignored = df_ignored[df_ignored["Konto_Nr_SKR51"].isna()]
  132. if not df_ignored.empty:
  133. df_ignored = df_ignored.pivot_table(
  134. index=["Konto_Nr_Händler"],
  135. columns=["period"],
  136. values="amount",
  137. aggfunc="sum",
  138. margins=True,
  139. margins_name="CumulatedYear",
  140. )
  141. df_ignored.to_csv(self.account_ignored, decimal=",", sep=";", encoding="latin-1")
  142. return export_filename
  143. @property
  144. def df_translate(self) -> pd.DataFrame:
  145. if self._df_translate is None:
  146. self.makes, self.sites, self._df_translate, self.df_translate2 = load_translation(
  147. self.account_translation, self.debug_file, self.export_invalid_filename
  148. )
  149. return self._df_translate
  150. @property
  151. def export_info_dir(self) -> str:
  152. return f"{self.base_dir}/Export/{self.current_year}/info/"
  153. @property
  154. def logs_dir(self) -> str:
  155. return f"{self.base_dir}/Logs/"
  156. @property
  157. def export_invalid_filename(self) -> str:
  158. return f"{self.base_dir}/Export/ungueltig.csv"
  159. def export_filename_for_period(self, year: str, month: str) -> str:
  160. return f"{self.base_dir}/Export/{year}/export_{year}-{month}.xml"
  161. def gchr_local() -> None:
  162. base_dir = os.getcwd() + "/../GCHR2_Testdaten/Kunden"
  163. for path in Path(base_dir).glob("*"):
  164. if not path.is_dir():
  165. continue
  166. print(path.name)
  167. gchr = GCHR(str(path))
  168. gchr.export_all_periods()