gchr.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import pandas as pd
  2. import numpy as np
  3. import xml.etree.ElementTree as ET
  4. import csv
  5. from xml.dom import minidom
  6. from datetime import datetime
  7. base_dir = '/home/robert/projekte/python/gcstruct/Siebrecht/'
  8. account_translation = base_dir + 'Kontenrahmen_kombiniert.csv'
  9. account_bookings = base_dir + 'GuV_Bilanz_Salden.csv'
  10. first_month_of_financial_year = '01'
  11. current_year = '2021'
  12. current_month = '12'
  13. account_ignored = base_dir + f'ignoriert_{current_year}-{current_month}.csv'
  14. account_invalid = base_dir + f'ungueltig_{current_year}-{current_month}.csv'
  15. last_year = str(int(current_year) - 1)
  16. next_year = str(int(current_year) + 1)
  17. def header(makes, sites):
  18. return {
  19. 'Country': 'DE',
  20. 'MainBmCode': sites[0]['Marke_HBV'],
  21. 'Month': current_month,
  22. 'Year': current_year,
  23. 'Currency': 'EUR',
  24. 'NumberOfMakes': len(makes),
  25. 'NumberOfSites': len(sites),
  26. 'ExtractionDate': datetime.now().strftime('%d.%m.%Y'),
  27. 'ExtractionTime': datetime.now().strftime('%H:%M:%S'),
  28. 'BeginFiscalYear': first_month_of_financial_year
  29. }
  30. def bookkeep_filter():
  31. period = [current_year + str(i).zfill(2) for i in range(1, 13)]
  32. if first_month_of_financial_year != '01':
  33. if first_month_of_financial_year > current_month:
  34. period = [last_year + str(i).zfill(2) for i in range(1, 13)] + period
  35. else:
  36. period = period + [next_year + str(i).zfill(2) for i in range(1, 13)]
  37. fm = int(first_month_of_financial_year)
  38. period = period[fm - 1:fm + 12]
  39. rename_to = ['Period' + str(i).zfill(2) for i in range(1, 13)]
  40. return dict(zip(period, rename_to))
  41. def export_month():
  42. # Übersetzungstabelle laden
  43. df_translate = pd.read_csv(account_translation, decimal=',', sep=';', encoding='latin-1',
  44. converters={i: str for i in range(0, 200)}, index_col='Konto_Nr_Händler')
  45. # Kontensalden laden
  46. df_bookings = pd.read_csv(account_bookings, decimal=',', sep=';', encoding='latin-1', converters={0: str, 1: str})
  47. # Kontensalden auf gegebenen Monat filtern
  48. filter_from = current_year + first_month_of_financial_year
  49. if first_month_of_financial_year > current_month:
  50. filter_from = last_year + first_month_of_financial_year
  51. filter_to = current_year + current_month
  52. df_bookings = df_bookings[(df_bookings['Bookkeep Period'] >= filter_from) & (df_bookings['Bookkeep Period'] <= filter_to)]
  53. bk_filter = bookkeep_filter()
  54. period_no = list(bk_filter.keys()).index(filter_to) + 1
  55. # Spalten konvertieren
  56. df_bookings['period'] = df_bookings['Bookkeep Period'].apply(lambda x: bk_filter[x])
  57. df_bookings['amount'] = df_bookings['Debit Amount'] + df_bookings['Credit Amount']
  58. df_bookings['amount'] = df_bookings['amount'].round(2)
  59. df_bookings['quantity'] = df_bookings['Debit Quantity'] + df_bookings['Credit Quantity']
  60. df_bookings['quantity'] = df_bookings['quantity'].round(2)
  61. # Join auf Übersetzung
  62. df_combined = df_bookings.merge(df_translate, how='inner', on='Konto_Nr_Händler')
  63. # df_combined.to_csv(account_invalid, decimal=',', sep=';', encoding='latin-1', index=False)
  64. # Gruppieren
  65. # df_grouped = df_combined.groupby(['Konto_Nr_SKR51', 'period']).sum()
  66. df_pivot = df_combined.pivot_table(index=['Konto_Nr_SKR51'], columns=['period'], values='amount',
  67. aggfunc=np.sum, margins=True, margins_name='CumulatedYear')
  68. # Infos ergänzen
  69. df_pivot['Decimals'] = 2
  70. df_pivot['OpeningBalance'] = 0.0
  71. df = df_translate.reset_index(drop=True).drop(columns=['Kostenträger_Ebene']).drop_duplicates()
  72. df = df.merge(df_pivot, how='inner', on='Konto_Nr_SKR51')
  73. # Bereinigung GW-Kostenträger
  74. df['GW_Verkauf_1'] = (df['Konto_Nr'].str.match(r'^[78]0')) & (df['Kostenstelle'].str.match(r'^[^1]\d'))
  75. df['Kostenstelle'] = np.where(df['GW_Verkauf_1'] == True, '11', df['Kostenstelle'])
  76. df['GW_Verkauf_2'] = (df['Konto_Nr'].str.match(r'^[78]1')) & (df['Kostenstelle'].str.match(r'^[^2]\d'))
  77. df['Kostenstelle'] = np.where(df['GW_Verkauf_2'] == True, '21', df['Kostenstelle'])
  78. df['GW_Verkauf_3'] = (df['Konto_Nr'].str.match(r'^[78]3')) & (df['Kostenstelle'].str.match(r'^[^3]\d'))
  79. df['Kostenstelle'] = np.where(df['GW_Verkauf_3'] == True, '31', df['Kostenstelle'])
  80. df['GW_Verkauf_4'] = (df['Konto_Nr'].str.match(r'^[78]4')) & (df['Kostenstelle'].str.match(r'^[^4]\d'))
  81. df['Kostenstelle'] = np.where(df['GW_Verkauf_4'] == True, '41', df['Kostenstelle'])
  82. df['GW_Verkauf_5'] = (df['Konto_Nr'].str.match(r'^[78]5')) & (df['Kostenstelle'].str.match(r'^[^5]\d'))
  83. df['Kostenstelle'] = np.where(df['GW_Verkauf_5'] == True, '51', df['Kostenstelle'])
  84. df['GW_Verkauf_50'] = (df['Konto_Nr'].str.match(r'^[78]')) & (df['Kostenstelle'].str.match(r'^2'))
  85. df['Kostenträger'] = np.where(df['GW_Verkauf_50'] == True, '52', df['Kostenträger'])
  86. df['Kostenträger'] = np.where((df['GW_Verkauf_50'] == True) & (df['Marke'] == '01'), '50', df['Kostenträger'])
  87. df['Bilanz'] = (df['Konto_Nr'].str.match(r'^[013]'))
  88. df['Kostenträger'] = np.where(df['Bilanz'] == True, '00', df['Kostenträger'])
  89. df['Konto_5er'] = (df['Konto_Nr'].str.match('^5'))
  90. df['Absatzkanal'] = np.where(df['Konto_5er'] == True, '99', df['Absatzkanal'])
  91. df['Teile_30_60'] = (df['Konto_Nr'].str.match(r'^[578]')) & \
  92. (df['Kostenstelle'].str.match(r'^[3]')) & \
  93. (df['Kostenträger'].str.match(r'^[^6]'))
  94. df['Kostenträger'] = np.where(df['Teile_30_60'] == True, '60', df['Kostenträger'])
  95. df['Service_40_70'] = (df['Konto_Nr'].str.match(r'^[578]')) & \
  96. (df['Kostenstelle'].str.match(r'^[4]')) & \
  97. (df['Kostenträger'].str.match(r'^[^7]'))
  98. df['Kostenträger'] = np.where(df['Service_40_70'] == True, '70', df['Kostenträger'])
  99. from_label = ['Marke', 'Standort', 'Konto_Nr', 'Kostenstelle', 'Absatzkanal', 'Kostenträger']
  100. to_label = ['Make', 'Site', 'Account', 'Origin', 'SalesChannel', 'CostCarrier']
  101. df = df.rename(columns=dict(zip(from_label, to_label)))
  102. df['CostAccountingString'] = df['Make'] + df['Site'] + df['Origin'] + \
  103. df['SalesChannel'] + df['CostCarrier']
  104. df['IsNumeric'] = df['CostAccountingString'].str.isdigit()
  105. df_invalid = df[df['IsNumeric'] == False]
  106. df_invalid.to_csv(account_invalid + '.2.csv', decimal=',', sep=';', encoding='latin-1', index=False)
  107. export_csv = base_dir + 'export_' + current_year + '-' + current_month + '.csv'
  108. df.to_csv(export_csv, decimal=',', sep=';', encoding='latin-1', index=False)
  109. export_file = export_xml(df, bk_filter, period_no)
  110. # Join auf Übersetzung - nicht zugeordnet
  111. df_ignored = df_bookings.merge(df_translate, how='left', on='Konto_Nr_Händler')
  112. df_ignored = df_ignored[df_ignored['Konto_Nr_SKR51'].isna()] # [['Konto_Nr_Händler', 'Bookkeep Period', 'amount', 'quantity']]
  113. if not df_ignored.empty:
  114. df_ignored = df_ignored.pivot_table(index=['Konto_Nr_Händler'], columns=['period'], values='amount',
  115. aggfunc=np.sum, margins=True, margins_name='CumulatedYear')
  116. df_ignored.to_csv(account_ignored, decimal=',', sep=';', encoding='latin-1')
  117. return export_file
  118. def export_xml(df: pd.DataFrame, bk_filter, period_no):
  119. df = df[df['IsNumeric']]
  120. makes = df[['Make', 'Marke_HBV']].drop_duplicates().sort_values(by=['Make']).to_dict(orient='records')
  121. sites = df[['Make', 'Site', 'Marke_HBV']].drop_duplicates().sort_values(by=['Make', 'Site']).to_dict(orient='records')
  122. res = df.to_dict(orient='records')
  123. record_elements = ['Account', 'Make', 'Site', 'Origin', 'SalesChannel', 'CostCarrier', 'CostAccountingString',
  124. 'Decimals', 'OpeningBalance'] + list(bk_filter.values())[:period_no] + ['CumulatedYear']
  125. root = ET.Element('HbvData')
  126. h = ET.SubElement(root, 'Header')
  127. for k, v in header(makes, sites).items():
  128. ET.SubElement(h, k).text = str(v)
  129. make_list = ET.SubElement(root, 'MakeList')
  130. for m in makes:
  131. e = ET.SubElement(make_list, 'MakeListEntry')
  132. ET.SubElement(e, 'Make').text = m['Make']
  133. ET.SubElement(e, 'MakeCode').text = m['Marke_HBV']
  134. bm_code_list = ET.SubElement(root, 'BmCodeList')
  135. for s in sites:
  136. e = ET.SubElement(bm_code_list, 'BmCodeEntry')
  137. ET.SubElement(e, 'Make').text = s['Make']
  138. ET.SubElement(e, 'Site').text = s['Site']
  139. ET.SubElement(e, 'BmCode').text = s['Marke_HBV']
  140. record_list = ET.SubElement(root, 'RecordList')
  141. for row in res:
  142. record = ET.SubElement(record_list, 'Record')
  143. for e in record_elements:
  144. child = ET.SubElement(record, e)
  145. field = row.get(e, 0.0)
  146. if str(field) == 'nan':
  147. field = '0'
  148. elif type(field) is float:
  149. field = '{:.0f}'.format(field * 100)
  150. child.text = str(field)
  151. export_file = base_dir + 'export_' + current_year + '-' + current_month + '.xml'
  152. with open(export_file, 'w', encoding='utf-8') as f:
  153. f.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=' '))
  154. return export_file
  155. def convert_to_row(node):
  156. return [child.text for child in node]
  157. def convert_xml_to_csv(xmlfile, csvfile):
  158. record_list = ET.parse(xmlfile).getroot().find('RecordList')
  159. header = [child.tag for child in record_list.find('Record')]
  160. bookings = [convert_to_row(node) for node in record_list.findall('Record')]
  161. with open(csvfile, 'w') as fwh:
  162. cwh = csv.writer(fwh, delimiter=';')
  163. cwh.writerow(header)
  164. cwh.writerows(bookings)
  165. return True
  166. if __name__ == '__main__':
  167. print(bookkeep_filter())
  168. export_file = export_month()
  169. # convert_xml_to_csv(export_file, export_file[:-4] + '.csv')
  170. # convert_xml_to_csv('DE2119_2105_150621_080944.xml', 'DE2119_2105_150621_080944.csv')