export_extf.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. from datetime import datetime
  2. import calendar
  3. import csv
  4. from pathlib import Path
  5. class DatevConfig:
  6. data_path: str = "datev/data"
  7. export_path: str = "datev/export"
  8. translation_file: str = "datev/data/uebersetzungstabelle.csv"
  9. csv_date: datetime = datetime.now() # datetime(2023, 11, 20, 19, 2, 28, 714000)
  10. geschaeftsjahr_beginn: datetime = datetime(2023, 1, 1)
  11. periode: str = "202301"
  12. berater: int = 30612
  13. mandant: int = 10139
  14. konto_laenge: int = 5
  15. @property
  16. def datum_von(self):
  17. return datetime(int(self.periode[:4]), int(self.periode[4:]), 1)
  18. @property
  19. def datum_bis(self):
  20. year = int(self.periode[:4])
  21. month = int(self.periode[4:])
  22. end_of_month = calendar.monthrange(year, month)[1]
  23. return datetime(year, month, end_of_month)
  24. @property
  25. def header2(self):
  26. res = [
  27. "Umsatz (ohne Soll/Haben-Kz)",
  28. "Soll/Haben-Kennzeichen",
  29. "WKZ Umsatz",
  30. "Kurs",
  31. "Basis-Umsatz",
  32. "WKZ Basis-Umsatz",
  33. "Konto",
  34. "Gegenkonto (ohne BU-Schlüssel)",
  35. "BU-Schlüssel",
  36. "Belegdatum",
  37. "Belegfeld 1",
  38. "Belegfeld 2",
  39. "Skonto",
  40. "Buchungstext",
  41. "Postensperre",
  42. "Diverse Adressnummer",
  43. "Geschäftspartnerbank",
  44. "Sachverhalt",
  45. "Zinssperre",
  46. "Beleglink",
  47. "Beleginfo - Art 1",
  48. "Beleginfo - Inhalt 1",
  49. "Beleginfo - Art 2",
  50. "Beleginfo - Inhalt 2",
  51. "Beleginfo - Art 3",
  52. "Beleginfo - Inhalt 3",
  53. "Beleginfo - Art 4",
  54. "Beleginfo - Inhalt 4",
  55. "Beleginfo - Art 5",
  56. "Beleginfo - Inhalt 5",
  57. "Beleginfo - Art 6",
  58. "Beleginfo - Inhalt 6",
  59. "Beleginfo - Art 7",
  60. "Beleginfo - Inhalt 7",
  61. "Beleginfo - Art 8",
  62. "Beleginfo - Inhalt 8",
  63. "KOST1 - Kostenstelle",
  64. "KOST2 - Kostenstelle",
  65. "Kost-Menge",
  66. "EU-Land u. UStID",
  67. "EU-Steuersatz",
  68. "Abw. Versteuerungsart",
  69. "Sachverhalt L+L",
  70. "Funktionsergänzung L+L",
  71. "BU 49 Hauptfunktionstyp",
  72. "BU 49 Hauptfunktionsnummer",
  73. "BU 49 Funktionsergänzung",
  74. "Zusatzinformation - Art 1",
  75. "Zusatzinformation- Inhalt 1",
  76. "Zusatzinformation - Art 2",
  77. "Zusatzinformation- Inhalt 2",
  78. "Zusatzinformation - Art 3",
  79. "Zusatzinformation- Inhalt 3",
  80. "Zusatzinformation - Art 4",
  81. "Zusatzinformation- Inhalt 4",
  82. "Zusatzinformation - Art 5",
  83. "Zusatzinformation- Inhalt 5",
  84. "Zusatzinformation - Art 6",
  85. "Zusatzinformation- Inhalt 6",
  86. "Zusatzinformation - Art 7",
  87. "Zusatzinformation- Inhalt 7",
  88. "Zusatzinformation - Art 8",
  89. "Zusatzinformation- Inhalt 8",
  90. "Zusatzinformation - Art 9",
  91. "Zusatzinformation- Inhalt 9",
  92. "Zusatzinformation - Art 10",
  93. "Zusatzinformation- Inhalt 10",
  94. "Zusatzinformation - Art 11",
  95. "Zusatzinformation- Inhalt 11",
  96. "Zusatzinformation - Art 12",
  97. "Zusatzinformation- Inhalt 12",
  98. "Zusatzinformation - Art 13",
  99. "Zusatzinformation- Inhalt 13",
  100. "Zusatzinformation - Art 14",
  101. "Zusatzinformation- Inhalt 14",
  102. "Zusatzinformation - Art 15",
  103. "Zusatzinformation- Inhalt 15",
  104. "Zusatzinformation - Art 16",
  105. "Zusatzinformation- Inhalt 16",
  106. "Zusatzinformation - Art 17",
  107. "Zusatzinformation- Inhalt 17",
  108. "Zusatzinformation - Art 18",
  109. "Zusatzinformation- Inhalt 18",
  110. "Zusatzinformation - Art 19",
  111. "Zusatzinformation- Inhalt 19",
  112. "Zusatzinformation - Art 20",
  113. "Zusatzinformation- Inhalt 20",
  114. "Stück",
  115. "Gewicht",
  116. "Zahlweise",
  117. "Forderungsart",
  118. "Veranlagungsjahr",
  119. "Zugeordnete Fälligkeit",
  120. "Skontotyp",
  121. "Auftragsnummer",
  122. "Buchungstyp",
  123. "Ust-Schlüssel (Anzahlungen)",
  124. "EU-Land (Anzahlungen)",
  125. "Sachverhalt L+L (Anzahlungen)",
  126. "EU-Steuersatz (Anzahlungen)",
  127. "Erlöskonto (Anzahlungen)",
  128. "Herkunft-Kz",
  129. "Leerfeld",
  130. "KOST-Datum",
  131. "Mandatsreferenz",
  132. "Skontosperre",
  133. "Gesellschaftername",
  134. "Beteiligtennummer",
  135. "Identifikationsnummer",
  136. "Zeichnernummer",
  137. "Postensperre bis",
  138. "Bezeichnung SoBil-Sachverhalt",
  139. "Kennzeichen SoBil-Buchung",
  140. "Festschreibung",
  141. "Leistungsdatum",
  142. "Datum Zuord.Steuerperiode",
  143. ]
  144. return ";".join(res)
  145. row_template = (
  146. '{0};"{1}";"{2}";;;"";"{9}";"{4}";"";{5};"{6}";"";;"{7}";;"";;;;"";"";'
  147. + '"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"{10}";"";;"";;"";;;;;;"";"";"";"";"";"";"";"";"";"";"";"";"";'
  148. + '"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";;;;"";;;;"";"";;"";;'
  149. + ';;"";"";;"";;"";;"";"";;"";;{8};;'
  150. )
  151. # '592.80;H;EUR;"15800";90900;0101;6288;Opel Bank VoST 12/22 Lagerwag;1'
  152. @property
  153. def export_file(self):
  154. timestamp = self.csv_date.strftime("%Y%m%d_%H%M%S")
  155. period = self.datum_von.strftime("%Y%m")
  156. return f"{self.export_path}/EXTF_Buchungsstapel_30612_10139_{period}_{timestamp}.csv"
  157. @property
  158. def header(self):
  159. datev_header = {
  160. "Datev-Format-KZ": "EXTF",
  161. "Versionsnummer": 510,
  162. "Datenkategorie": 21,
  163. "Formatname": "Buchungsstapel",
  164. "Formatversion": 7,
  165. "Erzeugt_am": self.csv_date.strftime("%Y%m%d%H%M%S%f")[:-3],
  166. "Importiert_am": "",
  167. "Herkunftskennzeichen": "SV",
  168. "Exportiert_von": "dracar",
  169. "Importiert_von": "",
  170. "Berater": self.berater,
  171. "Mandant": self.mandant,
  172. "WJ-Beginn": self.geschaeftsjahr_beginn.strftime("%Y%m%d"),
  173. "Sachkontenlänge": self.konto_laenge,
  174. "Datum_von": self.datum_von.strftime("%Y%m%d"),
  175. "Datum_bis": self.datum_bis.strftime("%Y%m%d"),
  176. "Bezeichnung": "",
  177. "Diktatkürzel": "HE",
  178. "Buchungstyp": 1,
  179. "Rechnungslegungszweck": "",
  180. "Festschreibeinformation": 1,
  181. "WKZ": "",
  182. "reserviert_1": "",
  183. "Derivatskennzeichen": "",
  184. "reserviert_2": "",
  185. "reserviert_3": "",
  186. "SKR": "",
  187. "Branchenlösung-Id": "",
  188. "reserviert_4": "",
  189. "reserviert_5": "",
  190. "Anwendungsinformation": "",
  191. }
  192. template = (
  193. '"EXTF";{Versionsnummer};{Datenkategorie};"Buchungsstapel";{Formatversion};{Erzeugt_am};'
  194. + ';"SV";"dracar";"";{Berater};{Mandant};{WJ-Beginn};{Sachkontenlänge};{Datum_von};{Datum_bis};"";"HE";1;;1;"";;"";;;"";;;"";""'
  195. )
  196. return template.format(**datev_header)
  197. def export_extf(period):
  198. cfg = DatevConfig()
  199. cfg.periode = period
  200. import_file = Path(f"datev/data/{period}.csv")
  201. cfg.csv_date = datetime.fromtimestamp(import_file.stat().st_mtime)
  202. with Path(cfg.translation_file).open("r", encoding="latin-1") as frh:
  203. translation = {}
  204. for line in csv.reader(frh, delimiter=";"):
  205. acct_no = line[0][:4] + "0"
  206. acct_details = "11" + line[0][11:].replace("-", "")
  207. translation[line[2]] = (acct_no, acct_details)
  208. missing = []
  209. with Path(cfg.export_file).open("w", encoding="latin-1", newline="") as fwh:
  210. fwh.write(cfg.header + "\r\n")
  211. fwh.write(cfg.header2 + "\r\n")
  212. with import_file.open("r", encoding="latin-1") as frh:
  213. csv_reader = csv.reader(frh, delimiter=";")
  214. next(csv_reader) # ignore header
  215. for row in csv_reader:
  216. row[0] = row[0].replace(".", ",")
  217. row.extend(translation.get(row[3], (row[3], "11000000")))
  218. if row[9] == row[3]:
  219. missing.append(row[3])
  220. fwh.write(cfg.row_template.format(*row) + "\r\n")
  221. print(set(missing))
  222. if __name__ == "__main__":
  223. export_extf("202301")
  224. export_extf("202302")