Package: doc-workflow
Creates, merges, splits, edits documents(mainly docx/pdf) as well as sending them by email.
Originally created for QR bills integration but is generic and can be used for much more.InstallationInstallation withpip:$ pip install doc-workflowUsageFrom the command line:$ docwf <path_to_json_config_file>From Python:from docwf import DocWorkflow

config_obj = {
    "globals": {
        "data": {
            "workbook": "source.xlsx",
            "sheet": "mailmergesheet",
        },
        "constants": {
            "language": "fr"
        }
    },
    "tasks": [
        {
            "active": 1, # you can activate/deactivate tasks
            "name": "create bills", # name for debug purpose
            "locals": {
                "data" : {
                    "sheet": "overridesheetfortask"
                },
                "key" : "value", # overrides global arguments for the task
            },
            "task": {
                "type": "myplugin", # or builtin plugins (see below)
                "task_dependent_argument": "value{param}",
            }
        },
    ]
}
my_plugins = {
    "myplugin": MyPluginClass
}
DocWorkflow(config_obj, plugins=my_plugins).gen()Typical workflow tasksAssume the data is in the source.xlsx in the sheet named billsclientnremailsend_emailtotalreferenceetc1c1@gmail.comyes1032ref2022c1…2c2@gmail.comyes1232ref2022c2…Create bills from Word template{
    "active": 1, # you can activate/deactivate tasks
    "name": "create bills", # name for debug purpose
    "task": {
        "type": "mailmerge",
        "input_docx": "templates/template_bill.docx",
        "output_docx": "bills/bill_{year}.docx" # output depends on the column year, it should be constant throughout all rows
    }
},Create pdf from the generated docxIt uses the Word Application (Mac/Windows).
If the docx template has dynamic fields (IF, etc),
the generated docx will ask permission to update
all fields before saving it as pdf.{
    "name": "save pdf from docx (uses Word)",
    "task": {
        "type": "makepdf",
        "input_docx": "bills/bill_{year}.docx",
        "output_pdf": "bills/bill_{year}.pdf"
    }
},Fills in QR codesfor the bills by adding a page to each bill or by merging the QR bill into one of the pages.{
    "name": "create qr bills",
    "locals": {
        "creditor": {
            "iban": "CH....",
            "name": "The Good Company",
            "pcode": "xyzt",
            "city": "Bern",
            "street": "Dorfstrasse 1"
        },
        "task_params": {
            "extra_infos": "reference", # fixed keys for bill reason ...
            "amount": "total"   # and the amount. With task_params you can create data entries out of existing columns
        }
    },
    "task": {
        "type": "qr",
        "merge_type": "merge", # or "append"
        "input_filename": "bills/bill_{year}.pdf",
        "delete_input": true, # delete the input filename after creating the output
        "pages": 2, # the number of pages per each bill
        "merge_pos": 2, # or "insert_pos" if "append"
        "output_filename": "bills/bill_{year}_with_qr.pdf"
    }
},Split the bills into separate pdf files.From one input to multiple outputs{
    "name": "split bills",
    "task": {
        "type": "split_pdf",
        "input_filename": "bills/bill_{year}_with_qr.pdf",
        "pages": 2,
        "makedir": "bills/bills_{year}", # if the output directory doesn't exist, create it
        "output_filename": "bills/bills_{year}/bill_{year}_{clientnr}.pdf" # output filename using unique name for each customer
    }
},Unify bills that are to be printedThis shows how to filter rows. The same split_pdf plugin is used, from multiple inputs to one output.{
    "name": "unify bills for print",
    "filter": {"column": "send_email", "value": "no"},
    "task": {
        "type": "split_pdf",
        "input_filename": "bills/bills_{year}/bill_{year}_{clientnr}.pdf",
        "delete_input": true,
        "pages": 2,
        "output_filename": "bills/bills_{year}_paper.pdf"
    }
},Send the bills by email{
    "name": "send emails",
    "locals": {
        "sender": {
            "email": "info@domain.com",
            "name": "Info",
            "server": "smtp.gmail.com:587",
            "username": "info@domain.com",
            "password": "strongpassword",
            "bcc": "bills@domain.com",
            "headers": {
                "Reply-To": "contability@domain.com"
            }
        },
    },
    "filter": {"column": "send_email", "value": "yes"},
    "task": {
        "type": "email",
        "recipient": "email", # the key/column name for the customer email
        "subject" : "Bill for year {year}", # can contain dynamic parts
        "body_template_file" : "templates/email_template.txt", # text template for the email body
        "attachments" : [ "bills/bills_{year}/bill_{year}_{clientnr}.pdf" ] # list of attachments
    }
},Watermark PDF filesMark reminder bills{
    "name": "save reminder",
    "filter": {"column": "reminder", "value": "yes"},
    "task": {
        "type": "watermark",
        "makedir": "bills/bills_{key_year}/reminders/",
        "watermark": "REMINDER",
        "input_filename": "bills/bills_{year}/bill_{year}_{clientnr}.pdf",
        "pages": 2,
        "output_filename": "bills/bills_{year}/reminders/bill_{year}_{clientnr}_reminder.pdf"
    }
},Send reminder bills{
    "name": "send reminder emails",
    "locals": {
        "sender": {
            ...
        },
    },
    "filter": [
        {"column": "send_email", "value": "yes"},
        {"column": "reminder", "value": "yes"}
    ],
    "task": {
        "type": "email",
        "recipient": "email", # the key/column name for the customer email
        "subject" : "Bill for year {year} (reminder)", # can contain dynamic parts
        "body_template_file" : "templates/reminder_email_template.txt", # text template for the email body
        "attachments" : [ "bills/bills_{year}/reminders/bill_{year}_{clientnr}_reminder.pdf" ] # list of attachments
    }
},Use Google Spreadsheets instead of ExcelTo support google spreadsheets you need a service account and credentials as JSON.
Follow the tutorialgspread with service account.Change the “workbook” value"globals": {
    "data": {
        "workbook": "https://docs.google.com/spreadsheets/d/1u...",
        "sheet": "mailmergesheet",
        "credentials": {
            "type": "service_account",
            "project_id": "...",
            "private_key_id": "...",
            "private_key": "-----BEGIN PRIVATE KEY....\n-----END PRIVATE KEY-----\n",
            "client_email": "project@project-123.iam.gserviceaccount.com",
            "client_id": "...",
            "auth_uri": "https://accounts.google.com/o/oauth2/auth",
            "token_uri": "https://oauth2.googleapis.com/token",
            "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
            "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
        }
    },
    ...
}Export Google Spreadsheets in a PDF fileOnly works with gspread type data{
    "#import": ["inc/inc_workbook_gspread.json"]
    "name": "export sheets as pdf",
    "globals": {
        "printsheets_defaults" : {
            "gridlines": true,
            "printnotes": false
        }
    },
    "tasks": [
        {
            "active": 1,
            "name": "bill documents",
            "task": {
                "makedir": "bills/web",
                "type": "printsheets",
                "printsheets": [
                    {
                        "gid": "1571231333"
                    },
                    {
                        "gid": "291382312357"
                    },
                    {
                        "gid": "3712318114",
                        "portrait": false,
                        "printnotes": true
                    }
                ],
                "output_filename": "bills/web/heizung_unterlagen_{key_year}.pdf"
            }
        }
    ]
}Todo / Wish ListCreate unit testsDevelop the command line to be able to run simple tasks directlyCreate more advanced filtersAuto-magically create directories (remove the makedir argument)ContributingFork the repository on GitHub and start hackingSend a pull request with your changesCreditsThis repository is created and maintained byIulian Ciorăscu.