Ver Fonte

-Fast Bills Beta 1.0

Danny há 2 anos atrás
pai
commit
6a9b1e6e46

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 #Installings
 
-pip install img2pdf styleframe xlsxwriter pysimplegui datetime SQLAlchemy
+pip install img2pdf styleframe xlsxwriter pysimplegui datetime SQLAlchemy docx2pdf
 conda install python-docx
 
 

+ 26 - 17
fast_excel_to_bill/bill_doc/docx_builder.py

@@ -1,8 +1,12 @@
-from docx.api import Document
+# from docx.api import Document
 from docx.oxml.table import CT_Tbl
 from docx.oxml.text.paragraph import CT_P
 from docx.table import _Cell, Table, _Row
 from docx.text.paragraph import Paragraph
+from docx import Document
+from docx.shared import Inches
+from docx.oxml import OxmlElement
+from docx.oxml.ns import qn
 import time
 import re
 
@@ -21,7 +25,7 @@ class DOCFileBuilder:
 
     # Low Level Functions
     def save_docx(self):
-        if self.docx_save_path == '': # Default
+        if self.docx_save_path == '':  # Default
             self.document.save()
         else:
             self.document.save(self.docx_save_path)
@@ -30,7 +34,7 @@ class DOCFileBuilder:
         self.header_ = self.document.sections[0].header
         self.footer_ = self.document.sections[0].footer
 
-    def replace_text_in_paragraph(self, paragraph,search_str, replace_str):
+    def replace_text_in_paragraph(self, paragraph, search_str, replace_str):
         inline = paragraph.runs
         for i in range(len(inline)):
             if search_str in inline[i].text:
@@ -46,11 +50,11 @@ class DOCFileBuilder:
 
     def replace_string_in_standard_text(self, search_str, replace_str):
         for p in self.document.paragraphs:
-            self.replace_text_in_paragraph(p,search_str,replace_str)
+            self.replace_text_in_paragraph(p, search_str, replace_str)
 
     def replace_string_in_all_tables(self, search_str, replace_str):
         for p in self.paragraphs_in_table():
-            self.replace_text_in_paragraph(p,search_str, replace_str)
+            self.replace_text_in_paragraph(p, search_str, replace_str)
 
     def replace_dates_string_in_standard_text(self, replace_date_str):
         for p in self.document.paragraphs:
@@ -120,12 +124,24 @@ class DOCFileBuilder:
                         inline = p.runs
                         for i in range(len(inline)):
                             if inline[i].text == '':
-                                inline[i].text = str(elem)
-                                count_columns += 1
+                                if isinstance(elem, list):
+                                    for list_idx,list_elem in enumerate(elem):
+                                        if list_idx == 0:
+                                            inline[i].text = str(elem[0])
+                                        else:
+                                            p.style = 'List'
+                                            r = p.add_run()
+                                            r.add_break()
+                                            r.add_text(str(list_elem))
+                                    count_columns += 1
+                                else:
+                                    inline[i].text = str(elem)
+                                    count_columns += 1
                 if count_columns == len(tuple):
                     len_insert_rows += 1
-                if len_insert_rows == len(list_tuple):
-                    return
+                    break
+            if len_insert_rows == len(list_tuple):
+                return
 
     # Custom Format functions
     def replace_Leistungszeitraum(self, start_date, end_date):
@@ -150,7 +166,7 @@ class DOCFileBuilder:
         for p in self.document.paragraphs:
             self.replace_text_in_paragraph(p, placeholder, replace_str)
         for p in self.paragraphs_in_table():
-            self.replace_text_in_paragraph(p, placeholder,replace_str)
+            self.replace_text_in_paragraph(p, placeholder, replace_str)
 
 
 def remove_row(table, row):
@@ -159,9 +175,6 @@ def remove_row(table, row):
     tbl.remove(tr)
 
 
-
-
-
 def iter_block_items(parent):
     """
     Yield each paragraph and table child within *parent*, in document order.
@@ -215,7 +228,3 @@ if __name__ == '__main__':
     key_table = builder_obj.search_table_with_key_columns(['KW', 'Leistungsübersicht'])
     builder_obj.delete_empty_rows_in_table(key_table, 2)
     doc = builder_obj.document
-
-
-
-

+ 18 - 4
fast_excel_to_bill/config_for_custom_bills.py

@@ -1,7 +1,21 @@
 # Docx Builder
-docx_input_path = r'C:\Users\Danny\Desktop\EnD and Investment\Tools\fast_excel_to_bill\test_folder\Templates\templeta.docx'
-docx_output_dir_path = r'C:\Users\Danny\Desktop\EnD and Investment\Tools\fast_excel_to_bill\test_folder'
-list_of_place_holders = ['?Netto?','?zzgl.?', '?gesamt?', '?insert_date?']
+DOCX_INPUT_PATH = r'C:\Users\Danny\Desktop\EnD and Investment\Tools\fast_excel_to_bill\test_folder\Templates\templeta.docx'
+LIST_OF_KEYS = ['Arbeitszeit', 'Task']
+ROUND_TIME = 300
+
+
+docx_output_dir_path = r'C:\Users\Danny\Desktop\EnD and Investment\Tools\fast_excel_to_bill\test_folder'# Variable cuz search of dir of customer can be implemeted
+list_of_place_holders = ['?Netto?','?zzgl.?', '?gesamt?', 'insertdate'] # variable cuz search can be implemented
 
 # Burden Date
-payment_request_range = 30
+payment_request_range = 30 # variable cuz customer contract data can be found
+income_per_hour = 28  # variable cuz search of employee core data
+
+FIRST_TABLE_KEYS = ['KW', 'Zeit in Stunden', 'Betrag in Euro']
+NUMBER_OF_COLUMNS_FIRST_TABLE = len(FIRST_TABLE_KEYS)
+
+SECOND_TABLE_KEYS = ['KW', 'Leistungsübersicht']
+NUMBER_OF_COLUMNS_SECOND_TABLE = len(SECOND_TABLE_KEYS) # variable cuz searching of number of columns can be done
+
+# Excel Pathes
+employee_worktime_table_pathes = [r'C:\Users\Danny\Desktop\EnD and Investment\Tools\time_recoder\time_recorded_tables\work_time_danny.xlsx'] # Variable cuz search of dir of tables can be implemented

+ 0 - 2
fast_excel_to_bill/excel_data_chris_format/table_config.py

@@ -1,2 +0,0 @@
-TABLE_PATH = r'C:\Users\Danny\Desktop\Tabel_Test\test.xlsx'
-LIST_OF_KEYS = ['BESCHREIBUNG','MENGE', 'EINZELPREIS', 'BETRAG']

+ 71 - 32
fast_excel_to_bill/main.py

@@ -2,56 +2,95 @@ from fast_excel_to_bill.bill_doc.docx_builder import DOCFileBuilder
 from fast_excel_to_bill import config_for_custom_bills
 from tool_lib import datetime_functions
 from datetime import datetime
+from fast_excel_to_bill.transform_excel_data_to_bill_data.time_recorder_format_main import main_data_collection, \
+    round_worktime_for_week, add_sum_to_dict, get_task_out_of_tree
 
 payment_request_range = config_for_custom_bills.payment_request_range
 year = int(datetime_functions.datetime_to_str(datetime_functions.get_current_date(), '%Y'))
-current_month_year= datetime_functions.datetime_to_str('current', '%m_%Y')
-docx_output_path = config_for_custom_bills.docx_output_dir_path+ r'\bill_{}.docx'.format(current_month_year)
+current_month_year = datetime_functions.datetime_to_str('current', '%m_%Y')
+docx_output_path = config_for_custom_bills.docx_output_dir_path + r'\bill_{}.docx'.format(current_month_year)
 
 
-def main():
+def get_excel_data():
     # Get Data which is necessary for the docx
-    # data_from_excel = ...
-    payment_request_date = datetime_functions.add_days_to_date(datetime_functions.get_current_date(),payment_request_range)
-    payment_request_date = datetime_functions.datetime_to_str(payment_request_date, '%d.%m.%Y')
-    # data_from_excel.append(payment_request_date)
+    bill_data_dict = main_data_collection(config_for_custom_bills.employee_worktime_table_pathes, config_for_custom_bills.LIST_OF_KEYS)
 
-    # Create a doc file which is based on a docx template
-    builder_obj = DOCFileBuilder(config_for_custom_bills.docx_input_path, docx_output_path)
+    # transform data to fomat
+    bill_data_dict, over_time = round_worktime_for_week(bill_data_dict, config_for_custom_bills.ROUND_TIME)
+    bill_data_dict = add_sum_to_dict(bill_data_dict, config_for_custom_bills.income_per_hour)
+    bill_data_dict = get_task_out_of_tree(bill_data_dict)
+    return bill_data_dict
 
-    # Set Date of Template
+
+def set_current_dates(builder_obj):
     current_date = datetime_functions.datetime_to_str(datetime_functions.get_current_date(), '%d.%m.%Y')
     builder_obj.replace_dates_string_in_standard_text(current_date)
     builder_obj.replace_dates_string_in_all_tables(current_date)
+    return builder_obj
 
-    # Set Leistungszeitraum
-    last_month = datetime_functions.get_current_month_number()-1
+
+def set_Leistungszeitraum(builder_obj):
+    last_month = datetime_functions.get_current_month_number() - 1
     month_range = datetime_functions.get_count_of_days_for_specific_month(year=year, month_number=last_month)
-    if last_month>9:
-        start_date = '01.'+ str(last_month) +'.'+ str(year)
+    if last_month > 9:
+        start_date = '01.' + str(last_month) + '.' + str(year)
     else:
         start_date = '01.' + '0' + str(last_month) + '.' + str(year)
-    end_date = datetime_functions.add_days_to_date(datetime.strptime(start_date,'%d.%m.%Y'), month_range-1)
+    end_date = datetime_functions.add_days_to_date(datetime.strptime(start_date, '%d.%m.%Y'), month_range - 1)
     end_date = datetime_functions.datetime_to_str(end_date, '%d.%m.%Y')
     builder_obj.replace_Leistungszeitraum(start_date, end_date)
-    builder_obj.save_docx()
+    return builder_obj
+
+
+def fill_table(builder_obj, key_list, data_list):
+    table = builder_obj.search_table_with_key_columns(key_list)
+    builder_obj.replace_rows(table, data_list)
+    builder_obj.delete_empty_rows_in_table(table, config_for_custom_bills.NUMBER_OF_COLUMNS_FIRST_TABLE)
+    return builder_obj
+
+
+def main():
+    # Get Data
+
+    bill_data_dict = get_excel_data()
+
+    # Create a doc file which is based on a docx template
+    builder_obj = DOCFileBuilder(config_for_custom_bills.DOCX_INPUT_PATH, docx_output_path)
+
+    # Set Date of Template
+    builder_obj = set_current_dates(builder_obj)
+
+    # Set Leistungszeitraum
+    builder_obj = set_Leistungszeitraum(builder_obj)
+
+    # Table KW/Time/ sum
+    data_list = []
+    for key in bill_data_dict.keys():
+        data_list += [(key, bill_data_dict[key][config_for_custom_bills.LIST_OF_KEYS[0]], bill_data_dict[key]['Betrag'])]
+
+    builder_obj = fill_table(builder_obj, config_for_custom_bills.FIRST_TABLE_KEYS, data_list)
+
+    # Table KW and Task
+    data_list = []
+    for key in bill_data_dict.keys():
+        data_list += [(key, bill_data_dict[key][config_for_custom_bills.LIST_OF_KEYS[1]])]
+
+    builder_obj = fill_table(builder_obj, config_for_custom_bills.SECOND_TABLE_KEYS, data_list)
+
     # Replace Placeholders
-    # place_holders = config_for_custom_bills.list_of_place_holders
-    # for place_holder in place_holders:
-    #     builder_obj.replace_custom_placeholder_in_doc(place_holder, data_from_excel)
-
-    # keys = ['KW', 'Leistung', 'Zeit in Stunden', 'Betrag in Euro']
-    # example_tuple = [(123123, 213123, 12312312, 445235)]
-    # key_table = builder_obj.search_table_with_key_columns(keys)
-    # builder_obj.replace_rows(key_table, example_tuple)
-    # builder_obj.delete_empty_rows_in_table(key_table, 4)
-    # builder_obj.document.add_page_break()
-    # key_table = builder_obj.search_table_with_key_columns(['KW', 'Leistungsübersicht'])
-    # builder_obj.delete_empty_rows_in_table(key_table, 2)
-    data_to_bill_doc = ...
-
-    # Transform the doc file to a pdf file
-    doc_to_pdf = ...
+
+    netto = sum([bill_data_dict[key]['Betrag'] for key in bill_data_dict.keys()])
+    tax = round(netto * 0.19, 2)
+    abs_amount = netto + tax
+    payment_request_date = datetime_functions.add_days_to_date(datetime_functions.get_current_date(), payment_request_range)
+    payment_request_date = datetime_functions.datetime_to_str(payment_request_date, '%d.%m.%Y')
+    place_holder_replacement_list = [netto, tax, abs_amount, payment_request_date]
+
+    place_holders = config_for_custom_bills.list_of_place_holders
+    for count, place_holder in enumerate(place_holders):
+        builder_obj.replace_custom_placeholder_in_doc(place_holder, str(place_holder_replacement_list[count]))
+
+    builder_obj.save_docx()
 
 
 if __name__ == '__main__':

BIN
fast_excel_to_bill/test_folder/Templates/templeta.docx


+ 0 - 0
fast_excel_to_bill/excel_data_chris_format/ReadMe → fast_excel_to_bill/transform_excel_data_to_bill_data/ReadMe


+ 1 - 1
fast_excel_to_bill/excel_data_chris_format/main.py → fast_excel_to_bill/transform_excel_data_to_bill_data/chris_format.py

@@ -1,5 +1,5 @@
 from tool_lib.read_table import Read_unordered_Table
-from fast_excel_to_bill.excel_data_chris_format.table_config import TABLE_PATH, LIST_OF_KEYS
+from fast_excel_to_bill.transform_excel_data_to_bill_data.table_config import TABLE_PATH, LIST_OF_KEYS
 
 
 def main():

+ 2 - 0
fast_excel_to_bill/transform_excel_data_to_bill_data/table_config.py

@@ -0,0 +1,2 @@
+TABLE_PATH = r'C:\Users\Danny\Desktop\EnD and Investment\Tools\time_recoder\time_recorded_tables\work_time_danny.xlsx'
+LIST_OF_KEYS = ['BESCHREIBUNG','MENGE', 'EINZELPREIS', 'BETRAG']

+ 136 - 0
fast_excel_to_bill/transform_excel_data_to_bill_data/time_recorder_format_main.py

@@ -0,0 +1,136 @@
+from tool_lib.read_table import Read_Table
+from fast_excel_to_bill.transform_excel_data_to_bill_data.table_config import TABLE_PATH
+
+
+def add_sum_to_dict(work_time_dict, income_per_hour):
+    for key in work_time_dict.keys():
+        work_time_dict[key]['Betrag'] = int(work_time_dict[key]['Arbeitszeit']*income_per_hour)
+    return work_time_dict
+
+def round_worktime_for_week(work_time_dict, round_time = 300):
+    for key in work_time_dict.keys():
+        over_time = work_time_dict[key]['Arbeitszeit'] % round_time # default round to 5 hours
+        work_time_dict[key]['Arbeitszeit'] = (work_time_dict[key]['Arbeitszeit']-over_time)/ 60
+    return work_time_dict, over_time
+
+def get_task_out_of_tree(work_time_dict):
+    for key in work_time_dict.keys():
+        work_time_dict[key]['Task'] = ['-   ' + last_branch[last_branch.rfind('/')+1:] for last_branch in work_time_dict[key]['Task']]
+    return work_time_dict
+
+
+
+
+def compress_data_by_key_list(*compressable_lists, key_list):
+    '''
+    function: a list containing different Values, if a Value is more than one time
+    in the list. Then this list an all list of the same size are compressed, for the values of the
+    list_of_same_size they are put in tuple.
+    '''
+    if len(compressable_lists) > 1:
+        for list_ in compressable_lists:
+            past_values = []
+            pos_bias = 0
+            for pos, value in enumerate(key_list):
+                if value in past_values:
+                    pos = pos - pos_bias
+                    if isinstance(list_[pos - pos_bias], int):
+                        list_[pos - 1] += list_[pos]
+                    elif isinstance(list_[pos - pos_bias], str):
+                        list_[pos - 1] = [list_[pos - 1], list_[pos]]
+                    elif isinstance(list_[pos - pos_bias], list):
+                        list_[pos - 1] = list_[pos - 1].append(list_[pos])
+                    list_.remove(list_[pos])
+                    pos_bias += 1
+                else:
+                    past_values += [value]
+    else:
+        compressable_lists = compressable_lists[0]
+        if len(compressable_lists) > len(key_list):
+            compressable_lists = compressable_lists[:len(key_list)]
+        elif len(compressable_lists) < len(key_list):
+            assert (False, 'Compressable list length should be greater equal then the length of the key_list')
+        past_values = []
+        pos_bias = 0
+        for pos, value in enumerate(key_list):
+            if value in past_values:
+                pos = pos - pos_bias
+                if isinstance(compressable_lists[pos - 1], int):
+                    compressable_lists[pos - 1] += compressable_lists[pos]
+                elif isinstance(compressable_lists[pos - 1], str):
+                    compressable_lists[pos - 1] = [compressable_lists[pos - 1], compressable_lists[pos]]
+                elif isinstance(compressable_lists[pos - 1], list):
+                    compressable_lists[pos - 1] += [compressable_lists[pos]]
+                compressable_lists.remove(compressable_lists[pos])
+                pos_bias += 1
+            else:
+                past_values += [value]
+    return compressable_lists
+
+
+def main_data_collection(pathes, list_of_keys=['Arbeitszeit', 'Task']):
+    week_keys_to_list_of_keys_dict = {}
+    if isinstance(pathes, list):
+        for c, path in enumerate(pathes):
+            # Read Data
+            read_table_object = Read_Table(path)
+            table_object = read_table_object.table_to_dict()
+
+            # Calendar Week
+            calendar_weeks_list = table_object['Kalenderwoche']
+
+            # Add new Weeks to dict
+            for week in list(set(calendar_weeks_list)):
+                if week in week_keys_to_list_of_keys_dict:
+                    pass
+                else:
+                    week_keys_to_list_of_keys_dict[week] = {}
+
+            # Get variable key dict with values of table
+            key_dict = {}
+            for key in list_of_keys:
+                key_dict[key] = table_object[key]
+
+            # Compress Data by Calendar week
+            for key in key_dict.keys():
+                all_weeks = compress_data_by_key_list(key_dict[key], key_list=calendar_weeks_list)
+                for week_indx, week in enumerate(list(set(calendar_weeks_list))):
+                    if c == 0:
+                        week_keys_to_list_of_keys_dict[week][key] = all_weeks[week_indx]
+                    else:
+                        if isinstance(all_weeks[week_indx], int):
+                            week_keys_to_list_of_keys_dict[week][key] += all_weeks[week_indx]
+                        elif isinstance(all_weeks[week_indx], list):
+                            week_keys_to_list_of_keys_dict[week][key] += all_weeks[week_indx]  # Change to Append if needed
+    else:
+        # Read Data
+        read_table_object = Read_Table(pathes)
+        table_object = read_table_object.table_to_dict()
+
+        # Calendar Week
+        calendar_weeks_list = table_object['Kalenderwoche']
+
+        # Add new Weeks to dict
+        for week in list(set(calendar_weeks_list)):
+            if week in week_keys_to_list_of_keys_dict:
+                pass
+            else:
+                week_keys_to_list_of_keys_dict[week] = {}
+
+        # Get variable key dict with values of table
+        key_dict = {}
+        for key in list_of_keys:
+            key_dict[key] = table_object[key]
+
+        # Compress Data by Calendar week
+        compressed_lists = []
+        for key in key_dict.keys():
+            all_weeks = compress_data_by_key_list(key_dict[key], key_list=calendar_weeks_list)
+            for week_indx, week in enumerate(list(set(calendar_weeks_list))):
+                week_keys_to_list_of_keys_dict[week][key] = all_weeks[week_indx]
+
+    return week_keys_to_list_of_keys_dict
+
+
+if __name__ == '__main__':
+    table_object = main_data_collection([TABLE_PATH, TABLE_PATH])