lexoffice.py added
This commit is contained in:
parent
8bef4f81e0
commit
59ca900a14
2
.gitignore
vendored
2
.gitignore
vendored
@ -294,3 +294,5 @@ $RECYCLE.BIN/
|
|||||||
# Windows shortcuts
|
# Windows shortcuts
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
|
# Sensitive details
|
||||||
|
config.yaml
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
Rechnungsnummer;Email;Anrede
|
Rechnungsnummer;Email;Anrede
|
||||||
RE357891;isaak.buslovich@trocknerheld.de;Isaak Trocknerheld
|
RE255111;fws-rs@vielhuber.eu;Sehr geehrter Herr Vielhuber
|
||||||
|
|||||||
|
58
app/lexoffice.py
Normal file
58
app/lexoffice.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
"""
|
||||||
|
Module: lexoffice.py
|
||||||
|
Description: Provides an interface to interact with the Lexoffice API.
|
||||||
|
This module enables operations such as retrieving invoice details from Lexoffice,
|
||||||
|
making it easier to integrate Lexoffice API functionality into a Flask application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import yaml
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class LexofficeAPI:
|
||||||
|
"""Class to handle interactions with the Lexoffice API."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize with the Lexoffice API access token from a config file."""
|
||||||
|
self.base_url = "https://api.lexoffice.io/v1"
|
||||||
|
self.headers = {
|
||||||
|
"Authorization": f"Bearer {self._load_api_token()}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _load_api_token() -> str:
|
||||||
|
"""Load the API token from the config.yaml file."""
|
||||||
|
config_path = Path(__file__).parent.parent / 'config.yaml'
|
||||||
|
with config_path.open('r') as file:
|
||||||
|
config = yaml.safe_load(file)
|
||||||
|
return config['lexoffice']['api_token']
|
||||||
|
|
||||||
|
def get_invoice_total_gross_amount(self, invoice_id: str) -> float:
|
||||||
|
"""
|
||||||
|
Retrieve the total gross amount for a specific invoice.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
invoice_id (str): Unique identifier of the invoice.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float or None: Total gross amount of the invoice, or None if an error occurs.
|
||||||
|
"""
|
||||||
|
url = f"{self.base_url}/invoices/{invoice_id}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=self.headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json().get('totalPrice', {}).get('totalGrossAmount')
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.error(f"Error fetching invoice: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Usage example:
|
||||||
|
# lexoffice_api = LexofficeAPI()
|
||||||
|
# total_gross_amount = lexoffice_api.get_invoice_total_gross_amount('your_invoice_id')
|
||||||
|
# print(total_gross_amount)
|
||||||
@ -1,8 +1,10 @@
|
|||||||
from flask import render_template, redirect, url_for, current_app, render_template_string, request
|
from flask import render_template, redirect, url_for, current_app, render_template_string, request
|
||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
from app import app, htmx
|
from app import app, htmx
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from app.lexoffice import LexofficeAPI
|
||||||
|
|
||||||
|
|
||||||
def read_invoices_from_csv(csv_file_path):
|
def read_invoices_from_csv(csv_file_path):
|
||||||
@ -10,10 +12,11 @@ def read_invoices_from_csv(csv_file_path):
|
|||||||
with open(csv_file_path, mode='r', encoding='utf-8') as file:
|
with open(csv_file_path, mode='r', encoding='utf-8') as file:
|
||||||
return list(csv.DictReader(file, delimiter=';'))
|
return list(csv.DictReader(file, delimiter=';'))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
current_app.logger.error(f"CSV file not found at {csv_file_path}")
|
logging.error(f"CSV file not found at {csv_file_path}")
|
||||||
|
return []
|
||||||
except csv.Error as e:
|
except csv.Error as e:
|
||||||
current_app.logger.error(f"Error reading CSV file: {e}")
|
logging.error(f"Error reading CSV file: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@app.route('/load-invoices')
|
@app.route('/load-invoices')
|
||||||
@ -48,31 +51,46 @@ def buchhaltung():
|
|||||||
@app.route('/add-invoice', methods=['POST'])
|
@app.route('/add-invoice', methods=['POST'])
|
||||||
def add_invoice():
|
def add_invoice():
|
||||||
csv_file_path = os.path.join(current_app.root_path, 'data', 'invoices.csv')
|
csv_file_path = os.path.join(current_app.root_path, 'data', 'invoices.csv')
|
||||||
|
|
||||||
|
# Extract invoice data from form
|
||||||
new_invoice = {
|
new_invoice = {
|
||||||
'Rechnungsnummer': request.form['rechnungsnummer'],
|
'Rechnungsnummer': request.form['rechnungsnummer'],
|
||||||
'Email': request.form['email'],
|
'Email': request.form['email'],
|
||||||
'Anrede': request.form['anrede']
|
'Anrede': request.form['anrede'],
|
||||||
|
# Initialize 'Betrag' with a placeholder (to be updated)
|
||||||
|
'Betrag': 'N/A'
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Read the existing data
|
# Fetch the total gross amount for the new invoice using LexofficeAPI
|
||||||
|
lexoffice_api = LexofficeAPI('your_access_token') # Replace with actual access token
|
||||||
|
total_gross_amount = lexoffice_api.get_invoice_total_gross_amount(new_invoice['Rechnungsnummer'])
|
||||||
|
|
||||||
|
# Update 'Betrag' in the new invoice data
|
||||||
|
if total_gross_amount is not None:
|
||||||
|
new_invoice['Betrag'] = total_gross_amount
|
||||||
|
|
||||||
|
# Read existing data from CSV
|
||||||
with open(csv_file_path, 'r', newline='', encoding='utf-8') as csvfile:
|
with open(csv_file_path, 'r', newline='', encoding='utf-8') as csvfile:
|
||||||
reader = csv.DictReader(csvfile, delimiter=';')
|
reader = csv.DictReader(csvfile, delimiter=';')
|
||||||
fieldnames = reader.fieldnames
|
fieldnames = reader.fieldnames
|
||||||
|
if 'Betrag' not in fieldnames:
|
||||||
|
fieldnames.append('Betrag')
|
||||||
existing_data = list(reader)
|
existing_data = list(reader)
|
||||||
|
|
||||||
# Add the new invoice at the beginning of the data
|
# Add the new invoice at the beginning of the data
|
||||||
existing_data.insert(0, new_invoice)
|
existing_data.insert(0, new_invoice)
|
||||||
|
|
||||||
# Write the updated data back to the file
|
# Write the updated data back to the CSV file
|
||||||
with open(csv_file_path, 'w', newline='', encoding='utf-8') as csvfile:
|
with open(csv_file_path, 'w', newline='', encoding='utf-8') as csvfile:
|
||||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=';')
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=';')
|
||||||
writer.writeheader()
|
writer.writeheader()
|
||||||
writer.writerows(existing_data)
|
writer.writerows(existing_data)
|
||||||
|
|
||||||
return redirect(url_for('buchhaltung'))
|
return redirect(url_for('buchhaltung'))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"Error updating CSV file: {e}")
|
logging.error(f"Error updating CSV file: {e}")
|
||||||
return "An error occurred while adding the invoice", 500
|
return "An error occurred while adding the invoice", 500
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* Import Google Fonts */
|
/* Import Google Fonts */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
|
||||||
|
|
||||||
/* Root and Variable Definitions */
|
/* Root and Variable Definitions */
|
||||||
:root {
|
:root {
|
||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
/* Body and Global Styles */
|
/* Body and Global Styles */
|
||||||
body {
|
body {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: var(--light-grey);
|
background: var(--light-grey);
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
<th>Rechnungsnummer</th>
|
<th>Rechnungsnummer</th>
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
<th>Anrede</th>
|
<th>Anrede</th>
|
||||||
|
<th>Betrag</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -13,6 +14,7 @@
|
|||||||
<td>{{ invoice['Rechnungsnummer'] }}</td>
|
<td>{{ invoice['Rechnungsnummer'] }}</td>
|
||||||
<td>{{ invoice['Email'] }}</td>
|
<td>{{ invoice['Email'] }}</td>
|
||||||
<td>{{ invoice['Anrede'] }}</td>
|
<td>{{ invoice['Anrede'] }}</td>
|
||||||
|
<td>{{ invoice['Betrag'] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
flask~=2.2.5
|
flask~=2.2.5
|
||||||
|
requests~=2.25.1
|
||||||
|
pyyaml~=6.0.1
|
||||||
|
|||||||
3
run.py
3
run.py
@ -1,4 +1,7 @@
|
|||||||
|
import logging
|
||||||
from app import app
|
from app import app
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
logging.basicConfig(filename='app.log', level=logging.DEBUG,
|
||||||
|
format='%(asctime)s %(levelname)s %(name)s : %(message)s')
|
||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user