implement currency and report
This commit is contained in:
@@ -66,6 +66,9 @@ class ArchivePage:
|
|||||||
def view_order(self, order):
|
def view_order(self, order):
|
||||||
#print(order)
|
#print(order)
|
||||||
user_id = self.page.session.get("user_id")
|
user_id = self.page.session.get("user_id")
|
||||||
|
user = self.get_user()
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
pdf_name = f'order_{user_id}_{order['order_number']}.pdf'
|
pdf_name = f'order_{user_id}_{order['order_number']}.pdf'
|
||||||
#print(pdf_name)
|
#print(pdf_name)
|
||||||
view_page = ViewPage(self.page, pdf_name, self.order_page, self.dashboard, order['id'])
|
view_page = ViewPage(self.page, pdf_name, self.order_page, self.dashboard, order['id'])
|
||||||
@@ -118,6 +121,12 @@ class ArchivePage:
|
|||||||
id = self.page.session.get("user_id")
|
id = self.page.session.get("user_id")
|
||||||
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
|
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
|
||||||
return True if response.json()['orders_out'] == 1 else False
|
return True if response.json()['orders_out'] == 1 else False
|
||||||
|
|
||||||
|
def get_user(self):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from pages.clients_page import ClientsPage
|
|||||||
from pages.transporters_page import TransportersPage
|
from pages.transporters_page import TransportersPage
|
||||||
from pages.destinations_page import DestinationsPage
|
from pages.destinations_page import DestinationsPage
|
||||||
from pages.orders_page import OrdersPage
|
from pages.orders_page import OrdersPage
|
||||||
from pages.report_page import ReportPage
|
from pages.reports_page import ReportsPage
|
||||||
from pages.profile_page import ProfilePage
|
from pages.profile_page import ProfilePage
|
||||||
from pages.subscription_page import Subscription
|
from pages.subscription_page import Subscription
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -356,7 +356,7 @@ class DashboardPage:
|
|||||||
orders = OrdersPage(self.page, self)
|
orders = OrdersPage(self.page, self)
|
||||||
self.placeholder.content = orders.build()
|
self.placeholder.content = orders.build()
|
||||||
elif index == 5:
|
elif index == 5:
|
||||||
reports = ReportPage(self.page, self)
|
reports = ReportsPage(self.page, self)
|
||||||
self.placeholder.content = reports.build()
|
self.placeholder.content = reports.build()
|
||||||
elif index == 6:
|
elif index == 6:
|
||||||
profile = ProfilePage(self.page, self)
|
profile = ProfilePage(self.page, self)
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ class OrdersEditPage:
|
|||||||
),
|
),
|
||||||
value = self.order['order_number']
|
value = self.order['order_number']
|
||||||
)
|
)
|
||||||
|
self.order_in_number = ft.TextField(
|
||||||
|
label="Order number",
|
||||||
|
value=self.order['order_in_number']
|
||||||
|
)
|
||||||
|
|
||||||
self.error_message = ft.Text(color = ft.Colors.RED)
|
self.error_message = ft.Text(color = ft.Colors.RED)
|
||||||
|
|
||||||
@@ -326,6 +330,31 @@ class OrdersEditPage:
|
|||||||
self.unloading_query = addresses
|
self.unloading_query = addresses
|
||||||
self.unloading.controls = self.create_unloading_list(addresses, self.on_delete_unloading_address_btn_click)
|
self.unloading.controls = self.create_unloading_list(addresses, self.on_delete_unloading_address_btn_click)
|
||||||
|
|
||||||
|
self.currency = ft.Dropdown(
|
||||||
|
editable=True,
|
||||||
|
label="Currency",
|
||||||
|
options=self.get_currency(),
|
||||||
|
value=self.order['currency'],
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_currency(self):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/currency/", headers=headers)
|
||||||
|
currency_list = response.json() if response.status_code == 200 else []
|
||||||
|
|
||||||
|
options = []
|
||||||
|
for currency in currency_list:
|
||||||
|
options.append(
|
||||||
|
ft.DropdownOption(
|
||||||
|
key=currency['name'],
|
||||||
|
content=ft.Text(
|
||||||
|
value=currency['name'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return options
|
||||||
|
|
||||||
def get_order(self):
|
def get_order(self):
|
||||||
try:
|
try:
|
||||||
token = self.page.client_storage.get("token")
|
token = self.page.client_storage.get("token")
|
||||||
@@ -883,6 +912,7 @@ class OrdersEditPage:
|
|||||||
|
|
||||||
saved_data = {
|
saved_data = {
|
||||||
'order_number': self.order_number.value,
|
'order_number': self.order_number.value,
|
||||||
|
'order_in_number': self.order_in_number.value,
|
||||||
'client_id': self.selected_client_id,
|
'client_id': self.selected_client_id,
|
||||||
'transporter_id': self.selected_transporter_id,
|
'transporter_id': self.selected_transporter_id,
|
||||||
'products_description': self.product_description.value,
|
'products_description': self.product_description.value,
|
||||||
@@ -893,7 +923,8 @@ class OrdersEditPage:
|
|||||||
'received_price': self.received_price.value,
|
'received_price': self.received_price.value,
|
||||||
'paid_price': self.paid_price.value,
|
'paid_price': self.paid_price.value,
|
||||||
'loading_addresses': loading_addresses,
|
'loading_addresses': loading_addresses,
|
||||||
'unloading_addresses': unloading_addresses
|
'unloading_addresses': unloading_addresses,
|
||||||
|
'currency': self.currency.value
|
||||||
}
|
}
|
||||||
#print(saved_data)
|
#print(saved_data)
|
||||||
if self.order_number.value == None or len(self.order_number.value)==0:
|
if self.order_number.value == None or len(self.order_number.value)==0:
|
||||||
@@ -1030,23 +1061,29 @@ class OrdersEditPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Column(
|
ft.Text('Edit Order', size=24, weight=ft.FontWeight.BOLD),
|
||||||
[
|
|
||||||
ft.Text('Edit Order', size=24, weight=ft.FontWeight.BOLD),
|
|
||||||
ft.Row(
|
|
||||||
[
|
|
||||||
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
|
|
||||||
self.order_number
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
|
||||||
alignment=ft.MainAxisAlignment.START
|
|
||||||
),
|
|
||||||
ft.ElevatedButton("Archive", on_click=self.on_archive_btn_click, width=150)
|
ft.ElevatedButton("Archive", on_click=self.on_archive_btn_click, width=150)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
vertical_alignment=ft.CrossAxisAlignment.START
|
vertical_alignment=ft.CrossAxisAlignment.START
|
||||||
),
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Order Out Number", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.order_number
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Order In Number", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.order_in_number
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Column(
|
ft.Column(
|
||||||
@@ -1151,7 +1188,8 @@ class OrdersEditPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD)
|
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.currency
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.START
|
alignment=ft.MainAxisAlignment.START
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -260,6 +260,31 @@ class OrdersInPage:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.filename = ft.Text()
|
self.filename = ft.Text()
|
||||||
|
|
||||||
|
self.currency = ft.Dropdown(
|
||||||
|
editable=True,
|
||||||
|
label="Currency",
|
||||||
|
options=self.get_currency(),
|
||||||
|
value="EURO",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_currency(self):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/currency/", headers=headers)
|
||||||
|
currency_list = response.json() if response.status_code == 200 else []
|
||||||
|
|
||||||
|
options = []
|
||||||
|
for currency in currency_list:
|
||||||
|
options.append(
|
||||||
|
ft.DropdownOption(
|
||||||
|
key=currency['name'],
|
||||||
|
content=ft.Text(
|
||||||
|
value=currency['name'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return options
|
||||||
|
|
||||||
def _open_date_picker(self, picker):
|
def _open_date_picker(self, picker):
|
||||||
# Works on both newer and older Flet
|
# Works on both newer and older Flet
|
||||||
@@ -817,6 +842,7 @@ class OrdersInPage:
|
|||||||
'unloading_addresses': unloading_addresses,
|
'unloading_addresses': unloading_addresses,
|
||||||
'file':self.filename.value,
|
'file':self.filename.value,
|
||||||
'expenses': self.expenses.value,
|
'expenses': self.expenses.value,
|
||||||
|
'currency': self.currency.value
|
||||||
}
|
}
|
||||||
#print(saved_data)
|
#print(saved_data)
|
||||||
if self.order_number.value == None or len(self.order_number.value)==0:
|
if self.order_number.value == None or len(self.order_number.value)==0:
|
||||||
@@ -1028,7 +1054,8 @@ class OrdersInPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Price / Expenses", size=18, weight=ft.FontWeight.BOLD)
|
ft.Text("Price / Expenses", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.currency
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.START
|
alignment=ft.MainAxisAlignment.START
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -213,6 +213,9 @@ class OrdersOutPage:
|
|||||||
),
|
),
|
||||||
value=number
|
value=number
|
||||||
)
|
)
|
||||||
|
self.order_in_number = ft.TextField(
|
||||||
|
label="Order number"
|
||||||
|
)
|
||||||
|
|
||||||
self.error_message = ft.Text(color = ft.Colors.RED)
|
self.error_message = ft.Text(color = ft.Colors.RED)
|
||||||
|
|
||||||
@@ -233,6 +236,30 @@ class OrdersOutPage:
|
|||||||
replacement_string=""
|
replacement_string=""
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
self.currency = ft.Dropdown(
|
||||||
|
editable=True,
|
||||||
|
label="Currency",
|
||||||
|
options=self.get_currency(),
|
||||||
|
value="EURO",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_currency(self):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/currency/", headers=headers)
|
||||||
|
currency_list = response.json() if response.status_code == 200 else []
|
||||||
|
|
||||||
|
options = []
|
||||||
|
for currency in currency_list:
|
||||||
|
options.append(
|
||||||
|
ft.DropdownOption(
|
||||||
|
key=currency['name'],
|
||||||
|
content=ft.Text(
|
||||||
|
value=currency['name'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return options
|
||||||
|
|
||||||
def load_orders(self):
|
def load_orders(self):
|
||||||
try:
|
try:
|
||||||
@@ -739,6 +766,7 @@ class OrdersOutPage:
|
|||||||
|
|
||||||
saved_data = {
|
saved_data = {
|
||||||
'order_number': self.order_number.value,
|
'order_number': self.order_number.value,
|
||||||
|
'order_in_number': self.order_in_number.value,
|
||||||
'client_id': self.selected_client_id,
|
'client_id': self.selected_client_id,
|
||||||
'transporter_id': self.selected_transporter_id,
|
'transporter_id': self.selected_transporter_id,
|
||||||
'products_description': self.product_description.value,
|
'products_description': self.product_description.value,
|
||||||
@@ -749,7 +777,8 @@ class OrdersOutPage:
|
|||||||
'received_price': self.received_price.value,
|
'received_price': self.received_price.value,
|
||||||
'paid_price': self.paid_price.value,
|
'paid_price': self.paid_price.value,
|
||||||
'loading_addresses': loading_addresses,
|
'loading_addresses': loading_addresses,
|
||||||
'unloading_addresses': unloading_addresses
|
'unloading_addresses': unloading_addresses,
|
||||||
|
'currency':self.currency.value
|
||||||
}
|
}
|
||||||
#print(saved_data)
|
#print(saved_data)
|
||||||
if self.order_number.value == None or len(str(self.order_number.value))==0:
|
if self.order_number.value == None or len(str(self.order_number.value))==0:
|
||||||
@@ -897,23 +926,29 @@ class OrdersOutPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Column(
|
ft.Text('Create Order Out', size=24, weight=ft.FontWeight.BOLD),
|
||||||
[
|
|
||||||
ft.Text('Create Order Out', size=24, weight=ft.FontWeight.BOLD),
|
|
||||||
ft.Row(
|
|
||||||
[
|
|
||||||
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
|
|
||||||
self.order_number
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
|
||||||
alignment=ft.MainAxisAlignment.START
|
|
||||||
),
|
|
||||||
ft.ElevatedButton("Archive", on_click=self.on_archive_btn_click, width=150)
|
ft.ElevatedButton("Archive", on_click=self.on_archive_btn_click, width=150)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
vertical_alignment=ft.CrossAxisAlignment.START
|
vertical_alignment=ft.CrossAxisAlignment.START
|
||||||
),
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Order Out Number", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.order_number
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Order In Number", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.order_in_number
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Column(
|
ft.Column(
|
||||||
@@ -1018,14 +1053,17 @@ class OrdersOutPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD)
|
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.currency
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.START
|
expand = True,
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
),
|
),
|
||||||
self.received_price,
|
self.received_price,
|
||||||
self.paid_price
|
self.paid_price
|
||||||
],
|
],
|
||||||
expand=2.5
|
expand=2.5,
|
||||||
|
alignment=ft.MainAxisAlignment.END
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
expand=True,
|
expand=True,
|
||||||
|
|||||||
572
transportmanager/client/pages/report_order_in_page.py
Normal file
572
transportmanager/client/pages/report_order_in_page.py
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
ISO = {"EURO": "EUR", "USD": "USD", "CHF": "CHF", "GBP": "GBP"}
|
||||||
|
|
||||||
|
def fetch_rates_for_base(base_ui: str) -> dict:
|
||||||
|
"""
|
||||||
|
Returns dict like {"EURO": 1.0, "USD": 1.0712, "CHF": 0.9573, "GBP": 0.8451}
|
||||||
|
meaning: 1 BASE = X other currency.
|
||||||
|
"""
|
||||||
|
base_iso = ISO[base_ui]
|
||||||
|
symbols = [c for c in ["EUR","USD","CHF","GBP"] if c != base_iso]
|
||||||
|
url = "https://api.frankfurter.app/latest"
|
||||||
|
params = {"from": base_iso, "to": ",".join(symbols)}
|
||||||
|
r = requests.get(url, params=params, timeout=6)
|
||||||
|
r.raise_for_status()
|
||||||
|
data = r.json()
|
||||||
|
rates = {k: float(v) for k, v in data["rates"].items()}
|
||||||
|
rates[base_iso] = 1.0
|
||||||
|
return {ui: rates[iso] for ui, iso in ISO.items()}
|
||||||
|
|
||||||
|
import flet as ft
|
||||||
|
import requests
|
||||||
|
from datetime import datetime
|
||||||
|
from config import API_BASE_URL
|
||||||
|
|
||||||
|
class ReportOrderInPage:
|
||||||
|
def __init__(self, page: ft.Page, dashboard):
|
||||||
|
self.page = page
|
||||||
|
self.dashboard = dashboard
|
||||||
|
self.start_date = ft.Text()
|
||||||
|
self.end_date = ft.Text()
|
||||||
|
# self.client_filter = ft.TextField(label="Client", expand=True)
|
||||||
|
# self.transporter_filter = ft.TextField(label="Transporter", expand=True)
|
||||||
|
self.status_text = ft.Text("")
|
||||||
|
self.results_text = ft.Text("")
|
||||||
|
self.rows = []
|
||||||
|
self.rows_copy = []
|
||||||
|
self.total = ft.Text("Total: ", weight=ft.FontWeight.BOLD)
|
||||||
|
self.currency_list = []
|
||||||
|
self.convert_currency_placeholder = ft.Container()
|
||||||
|
self.convert_currency = ft.Button("Convert Currency", on_click=self.on_convert_curency_btn_click)
|
||||||
|
self.data_table = ft.DataTable(
|
||||||
|
columns=[
|
||||||
|
ft.DataColumn(label=ft.Text("Order #")),
|
||||||
|
ft.DataColumn(label=ft.Text("Client")),
|
||||||
|
ft.DataColumn(label=ft.Text("Date")),
|
||||||
|
ft.DataColumn(label=ft.Text("Expenses")),
|
||||||
|
ft.DataColumn(label=ft.Text("Received")),
|
||||||
|
ft.DataColumn(label=ft.Text("Profit")),
|
||||||
|
ft.DataColumn(label=ft.Text("Currency")),
|
||||||
|
],
|
||||||
|
rows=[],
|
||||||
|
border=ft.border.all(1, ft.Colors.GREY_300),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.all_clients = []
|
||||||
|
self.all_transporters = []
|
||||||
|
self.create_table_rows_data()
|
||||||
|
|
||||||
|
self.clients_filter = ft.Dropdown(
|
||||||
|
options=[
|
||||||
|
ft.dropdown.Option(text = client['name'], key=client['name']) for client in self.all_clients
|
||||||
|
],
|
||||||
|
width=250,
|
||||||
|
label="Clients",
|
||||||
|
hint_text= "Select client",
|
||||||
|
on_change= self.filter_by_client
|
||||||
|
)
|
||||||
|
self.clients_filter_placeholder = ft.Container(content=self.clients_filter)
|
||||||
|
|
||||||
|
self.convert_courrency_dialog_placeholder = ft.Column()
|
||||||
|
self.convert_currency_choice = ft.RadioGroup(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Radio(value="USD", label="USD"),
|
||||||
|
ft.Radio(value="EURO", label="EURO"),
|
||||||
|
ft.Radio(value="CHF", label="CHF"),
|
||||||
|
ft.Radio(value="GBP", label="GBP"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
on_change=self.radiogroup_changed,
|
||||||
|
)
|
||||||
|
self.convert_courency_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Select Currency"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self.convert_currency_choice,
|
||||||
|
self.convert_courrency_dialog_placeholder
|
||||||
|
],
|
||||||
|
width=400,
|
||||||
|
height=300
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.TextButton("Cancel", on_click=self.on_cancel_btn_click),
|
||||||
|
ft.Button("Confirm", on_click=self.on_confirm_btn_click)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.euro = ft.TextField(
|
||||||
|
label="EURO",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.usd = ft.TextField(
|
||||||
|
label="USD",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.chf = ft.TextField(
|
||||||
|
label="CHF",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.gbp = ft.TextField(
|
||||||
|
label="GBP",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
|
||||||
|
def radiogroup_changed(self, e):
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.clear()
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
ft.Text(f"Sets the currency exchange rates in relation to {self.convert_currency_choice.value}"),
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'USD':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.euro,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.chf,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.gbp,
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'EURO':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.usd,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.chf,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.gbp,
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'CHF':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.usd,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.euro,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.gbp,
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'GBP':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.usd,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.euro,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.chf,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
fx = fetch_rates_for_base(self.convert_currency_choice.value)
|
||||||
|
self.euro.value = f"{fx['EURO']:.4f}"
|
||||||
|
self.usd.value = f"{fx['USD']:.4f}"
|
||||||
|
self.chf.value = f"{fx['CHF']:.4f}"
|
||||||
|
self.gbp.value = f"{fx['GBP']:.4f}"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.convert_courrency_dialog_placeholder.update()
|
||||||
|
|
||||||
|
def on_convert_curency_btn_click(self, e):
|
||||||
|
self.page.open(self.convert_courency_dialog)
|
||||||
|
base = self.convert_currency_choice.value or "EURO"
|
||||||
|
try:
|
||||||
|
fx = fetch_rates_for_base(base)
|
||||||
|
self.euro.value = f"{fx['EURO']:.4f}"
|
||||||
|
self.usd.value = f"{fx['USD']:.4f}"
|
||||||
|
self.chf.value = f"{fx['CHF']:.4f}"
|
||||||
|
self.gbp.value = f"{fx['GBP']:.4f}"
|
||||||
|
self.convert_courrency_dialog_placeholder.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.page.snack_bar = ft.SnackBar(ft.Text(f"FX fetch failed: {ex}"))
|
||||||
|
self.page.snack_bar.open = True
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.convert_courency_dialog)
|
||||||
|
|
||||||
|
def on_confirm_btn_click(self, e):
|
||||||
|
self.page.close(self.convert_courency_dialog)
|
||||||
|
# Build rates relative to the selected base currency.
|
||||||
|
# Example: if base is EURO, then rates['USD'] should be "1 EURO = X USD".
|
||||||
|
# To convert an amount from USD -> EURO we divide by rates['USD'].
|
||||||
|
base = self.convert_currency_choice.value
|
||||||
|
# Normalize and validate rate inputs
|
||||||
|
def _to_float(v):
|
||||||
|
try:
|
||||||
|
return float(v)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
rates = {
|
||||||
|
'EURO': _to_float(self.euro.value),
|
||||||
|
'USD': _to_float(self.usd.value),
|
||||||
|
'CHF': _to_float(self.chf.value),
|
||||||
|
'GBP': _to_float(self.gbp.value),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper to convert any amount to the selected base currency
|
||||||
|
def to_base(amount, currency):
|
||||||
|
try:
|
||||||
|
amt = float(amount)
|
||||||
|
except Exception:
|
||||||
|
return 0.0
|
||||||
|
if currency == base:
|
||||||
|
return amt
|
||||||
|
rate = rates.get(currency)
|
||||||
|
# If the rate is missing or invalid, keep original (safer than crashing)
|
||||||
|
if not rate or rate == 0:
|
||||||
|
return amt
|
||||||
|
# Convert from currency -> base by dividing when rates are "1 base = rate[currency] currency"
|
||||||
|
return amt / rate
|
||||||
|
|
||||||
|
# Rebuild table in base currency
|
||||||
|
self.rows_copy = list(self.rows)
|
||||||
|
total = 0.0
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
|
||||||
|
for r in self.rows_copy:
|
||||||
|
# r structure: [order_number, client_name, transporter_name, order_date, paid, received, profit, currency]
|
||||||
|
original_currency = r[6]
|
||||||
|
|
||||||
|
paid_base = to_base(r[3], original_currency)
|
||||||
|
received_base = to_base(r[4], original_currency)
|
||||||
|
profit_base = round(float(received_base) - float(paid_base), 2)
|
||||||
|
|
||||||
|
# Update a display row (do not mutate self.rows source amounts)
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(f"{paid_base:.2f}")),
|
||||||
|
ft.DataCell(ft.Text(f"{received_base:.2f}")),
|
||||||
|
ft.DataCell(ft.Text(f"{profit_base:.2f}")),
|
||||||
|
ft.DataCell(ft.Text(base)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
total += profit_base
|
||||||
|
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total:.2f} {base}"
|
||||||
|
self.total.update()
|
||||||
|
|
||||||
|
def get_orders(self):
|
||||||
|
try:
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/orders_in/list", headers=headers)
|
||||||
|
#print(response.text)
|
||||||
|
return response.json() if response.status_code == 200 else []
|
||||||
|
except Exception as e:
|
||||||
|
print("Error loading orders:", e)
|
||||||
|
|
||||||
|
def get_client(self, id):
|
||||||
|
try:
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/clients/{id}", headers=headers)
|
||||||
|
if response.json() not in self.all_clients:
|
||||||
|
self.all_clients.append(response.json())
|
||||||
|
return response.json() if response.status_code == 200 else None
|
||||||
|
except Exception as e:
|
||||||
|
print("Error loading clients:", e)
|
||||||
|
|
||||||
|
def fetch_report(self, e):
|
||||||
|
total = 0
|
||||||
|
data = self.search_input.value
|
||||||
|
self.rows_copy = self.rows
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
buffer = []
|
||||||
|
for r in self.rows_copy:
|
||||||
|
if data in r[1]:
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(r[3])),
|
||||||
|
ft.DataCell(ft.Text(r[4])),
|
||||||
|
ft.DataCell(ft.Text(r[5])),
|
||||||
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
buffer.append(r)
|
||||||
|
total += r[5]
|
||||||
|
self.rows_copy = buffer
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
|
self.total.update()
|
||||||
|
|
||||||
|
def create_table_rows_data(self):
|
||||||
|
all_orders = self.get_orders()
|
||||||
|
print(all_orders)
|
||||||
|
total = 0
|
||||||
|
for order in all_orders:
|
||||||
|
if order['currency'] not in self.currency_list:
|
||||||
|
self.currency_list.append(order['currency'])
|
||||||
|
if len(self.currency_list) > 1:
|
||||||
|
self.convert_currency_placeholder.content=self.convert_currency
|
||||||
|
order_number = order['order_number']
|
||||||
|
client_name = self.get_client(order['client_id'])['name']
|
||||||
|
order_date = order['created_at'].split("T")[0]
|
||||||
|
paid = order['expenses']
|
||||||
|
received = order['received_price']
|
||||||
|
currency = order['currency']
|
||||||
|
try:
|
||||||
|
profit = round(float(received) - float(paid), 2)
|
||||||
|
except:
|
||||||
|
profit = 0.00
|
||||||
|
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(order_number)),
|
||||||
|
ft.DataCell(ft.Text(client_name)),
|
||||||
|
ft.DataCell(ft.Text(order_date)),
|
||||||
|
ft.DataCell(ft.Text(paid)),
|
||||||
|
ft.DataCell(ft.Text(received)),
|
||||||
|
ft.DataCell(ft.Text(profit)),
|
||||||
|
ft.DataCell(ft.Text(currency)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
row_data = [order_number, client_name, order_date, paid, received, profit, currency]
|
||||||
|
self.rows.append(row_data)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
total += profit
|
||||||
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
|
self.rows_copy = self.rows
|
||||||
|
|
||||||
|
def on_reset_btn_click(self, e):
|
||||||
|
# --- Recreate Clients dropdown to avoid sticky selection text on Flet 0.28.3 ---
|
||||||
|
try:
|
||||||
|
client_options = list(self.clients_filter.options)
|
||||||
|
except Exception:
|
||||||
|
client_options = []
|
||||||
|
new_clients_dd = ft.Dropdown(
|
||||||
|
options=client_options,
|
||||||
|
width=250,
|
||||||
|
label="Clients",
|
||||||
|
hint_text="Select client",
|
||||||
|
on_change=self.filter_by_client,
|
||||||
|
value=None,
|
||||||
|
)
|
||||||
|
self.clients_filter = new_clients_dd
|
||||||
|
self.clients_filter_placeholder.content = self.clients_filter
|
||||||
|
self.clients_filter_placeholder.update()
|
||||||
|
|
||||||
|
self.page.update()
|
||||||
|
self.rows_copy = list(self.rows)
|
||||||
|
total = 0
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
for r in self.rows_copy:
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(r[3])),
|
||||||
|
ft.DataCell(ft.Text(r[4])),
|
||||||
|
ft.DataCell(ft.Text(r[5])),
|
||||||
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
total += r[5]
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
|
self.total.update()
|
||||||
|
self.start_date.value = ""
|
||||||
|
self.start_date.update()
|
||||||
|
self.end_date.value = ""
|
||||||
|
self.end_date.update()
|
||||||
|
|
||||||
|
def on_start_date_click(self, e):
|
||||||
|
self.start_date.value = e.control.value.strftime('%Y-%m-%d')
|
||||||
|
self.start_date.update()
|
||||||
|
total = 0
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
buffer = []
|
||||||
|
data = datetime.strptime(self.start_date.value, '%Y-%m-%d')
|
||||||
|
for r in self.rows_copy:
|
||||||
|
obj_date = datetime.strptime(r[3], '%Y-%m-%d')
|
||||||
|
if data <= obj_date:
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(r[3])),
|
||||||
|
ft.DataCell(ft.Text(r[4])),
|
||||||
|
ft.DataCell(ft.Text(r[5])),
|
||||||
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
buffer.append(r)
|
||||||
|
total += r[5]
|
||||||
|
self.rows_copy = buffer
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
|
self.total.update()
|
||||||
|
|
||||||
|
def on_end_date_click(self, e):
|
||||||
|
self.end_date.value = e.control.value.strftime('%Y-%m-%d')
|
||||||
|
self.end_date.update()
|
||||||
|
total = 0
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
buffer = []
|
||||||
|
data = datetime.strptime(self.end_date.value, '%Y-%m-%d')
|
||||||
|
for r in self.rows_copy:
|
||||||
|
obj_date = datetime.strptime(r[3], '%Y-%m-%d')
|
||||||
|
if data >= obj_date:
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(r[3])),
|
||||||
|
ft.DataCell(ft.Text(r[4])),
|
||||||
|
ft.DataCell(ft.Text(r[5])),
|
||||||
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
buffer.append(r)
|
||||||
|
total += r[5]
|
||||||
|
self.rows_copy = buffer
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
|
self.total.update()
|
||||||
|
|
||||||
|
def filter_by_client(self, e):
|
||||||
|
total = 0
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
buffer = []
|
||||||
|
for r in self.rows_copy:
|
||||||
|
#print(r[1])
|
||||||
|
#print(self.clients_filter.value)
|
||||||
|
if r[1] == self.clients_filter.value:
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(r[3])),
|
||||||
|
ft.DataCell(ft.Text(r[4])),
|
||||||
|
ft.DataCell(ft.Text(r[5])),
|
||||||
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
buffer.append(r)
|
||||||
|
total += r[5]
|
||||||
|
self.rows_copy = buffer
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
|
self.total.update()
|
||||||
|
|
||||||
|
def get_client_access(self):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
|
||||||
|
user = response.json()
|
||||||
|
if user['user_role'] == 'user':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
id = self.page.session.get("user_id")
|
||||||
|
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
|
||||||
|
return True if response.json()['report'] == 1 else False
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Report Orders In", size=24, weight=ft.FontWeight.BOLD),
|
||||||
|
self.total
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.start_date,
|
||||||
|
ft.ElevatedButton(
|
||||||
|
"Start Date",
|
||||||
|
on_click=lambda _: self.page.open(ft.DatePicker(
|
||||||
|
first_date=datetime(year=2000, month=10, day=1),
|
||||||
|
last_date=datetime(year=2025, month=10, day=1),
|
||||||
|
on_change=self.on_start_date_click,
|
||||||
|
)),
|
||||||
|
width=120,
|
||||||
|
icon=ft.Icons.CALENDAR_MONTH
|
||||||
|
),
|
||||||
|
self.end_date,
|
||||||
|
ft.ElevatedButton(
|
||||||
|
"End Date",
|
||||||
|
on_click=lambda _: self.page.open(ft.DatePicker(
|
||||||
|
first_date=datetime(year=2000, month=10, day=1),
|
||||||
|
last_date=datetime(year=2025, month=10, day=1),
|
||||||
|
on_change=self.on_end_date_click,
|
||||||
|
)),
|
||||||
|
width=120,
|
||||||
|
icon=ft.Icons.CALENDAR_MONTH
|
||||||
|
),
|
||||||
|
ft.Text(),
|
||||||
|
self.clients_filter_placeholder,
|
||||||
|
ft.ElevatedButton("Reset", on_click=self.on_reset_btn_click, width=120),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
self.convert_currency_placeholder
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.data_table,
|
||||||
|
self.status_text,
|
||||||
|
],
|
||||||
|
expand=True,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
expand=True,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Reports Order In", size=24, weight=ft.FontWeight.BOLD),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"You do not have access to this page content",
|
||||||
|
size=24,
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
color=ft.Colors.RED
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.Text("")
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
@@ -1,9 +1,27 @@
|
|||||||
|
ISO = {"EURO": "EUR", "USD": "USD", "CHF": "CHF", "GBP": "GBP"}
|
||||||
|
|
||||||
|
def fetch_rates_for_base(base_ui: str) -> dict:
|
||||||
|
"""
|
||||||
|
Returns dict like {"EURO": 1.0, "USD": 1.0712, "CHF": 0.9573, "GBP": 0.8451}
|
||||||
|
meaning: 1 BASE = X other currency.
|
||||||
|
"""
|
||||||
|
base_iso = ISO[base_ui]
|
||||||
|
symbols = [c for c in ["EUR","USD","CHF","GBP"] if c != base_iso]
|
||||||
|
url = "https://api.frankfurter.app/latest"
|
||||||
|
params = {"from": base_iso, "to": ",".join(symbols)}
|
||||||
|
r = requests.get(url, params=params, timeout=6)
|
||||||
|
r.raise_for_status()
|
||||||
|
data = r.json()
|
||||||
|
rates = {k: float(v) for k, v in data["rates"].items()}
|
||||||
|
rates[base_iso] = 1.0
|
||||||
|
return {ui: rates[iso] for ui, iso in ISO.items()}
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
import requests
|
import requests
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from config import API_BASE_URL
|
from config import API_BASE_URL
|
||||||
|
|
||||||
class ReportPage:
|
class ReportOrderOutPage:
|
||||||
def __init__(self, page: ft.Page, dashboard):
|
def __init__(self, page: ft.Page, dashboard):
|
||||||
self.page = page
|
self.page = page
|
||||||
self.dashboard = dashboard
|
self.dashboard = dashboard
|
||||||
@@ -16,16 +34,19 @@ class ReportPage:
|
|||||||
self.rows = []
|
self.rows = []
|
||||||
self.rows_copy = []
|
self.rows_copy = []
|
||||||
self.total = ft.Text("Total: ", weight=ft.FontWeight.BOLD)
|
self.total = ft.Text("Total: ", weight=ft.FontWeight.BOLD)
|
||||||
|
self.currency_list = []
|
||||||
|
self.convert_currency_placeholder = ft.Container()
|
||||||
|
self.convert_currency = ft.Button("Convert Currency", on_click=self.on_convert_curency_btn_click)
|
||||||
self.data_table = ft.DataTable(
|
self.data_table = ft.DataTable(
|
||||||
columns=[
|
columns=[
|
||||||
ft.DataColumn(label=ft.Text("Order #")),
|
ft.DataColumn(label=ft.Text("Order #")),
|
||||||
ft.DataColumn(label=ft.Text("Client")),
|
ft.DataColumn(label=ft.Text("Client")),
|
||||||
ft.DataColumn(label=ft.Text("Transporter")),
|
ft.DataColumn(label=ft.Text("Transporter")),
|
||||||
ft.DataColumn(label=ft.Text("Date")),
|
ft.DataColumn(label=ft.Text("Date")),
|
||||||
ft.DataColumn(label=ft.Text("Paid (€)")),
|
ft.DataColumn(label=ft.Text("Paid")),
|
||||||
ft.DataColumn(label=ft.Text("Received (€)")),
|
ft.DataColumn(label=ft.Text("Received")),
|
||||||
ft.DataColumn(label=ft.Text("Profit (€)")),
|
ft.DataColumn(label=ft.Text("Profit")),
|
||||||
|
ft.DataColumn(label=ft.Text("Currency")),
|
||||||
],
|
],
|
||||||
rows=[],
|
rows=[],
|
||||||
border=ft.border.all(1, ft.Colors.GREY_300),
|
border=ft.border.all(1, ft.Colors.GREY_300),
|
||||||
@@ -58,6 +79,191 @@ class ReportPage:
|
|||||||
)
|
)
|
||||||
self.transporters_filter_placeholder = ft.Container(content=self.transporters_filter)
|
self.transporters_filter_placeholder = ft.Container(content=self.transporters_filter)
|
||||||
|
|
||||||
|
self.convert_courrency_dialog_placeholder = ft.Column()
|
||||||
|
self.convert_currency_choice = ft.RadioGroup(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Radio(value="USD", label="USD"),
|
||||||
|
ft.Radio(value="EURO", label="EURO"),
|
||||||
|
ft.Radio(value="CHF", label="CHF"),
|
||||||
|
ft.Radio(value="GBP", label="GBP"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
on_change=self.radiogroup_changed,
|
||||||
|
)
|
||||||
|
self.convert_courency_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Select Currency"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self.convert_currency_choice,
|
||||||
|
self.convert_courrency_dialog_placeholder
|
||||||
|
],
|
||||||
|
width=400,
|
||||||
|
height=300
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.TextButton("Cancel", on_click=self.on_cancel_btn_click),
|
||||||
|
ft.Button("Confirm", on_click=self.on_confirm_btn_click)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.euro = ft.TextField(
|
||||||
|
label="EURO",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.usd = ft.TextField(
|
||||||
|
label="USD",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.chf = ft.TextField(
|
||||||
|
label="CHF",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.gbp = ft.TextField(
|
||||||
|
label="GBP",
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
|
||||||
|
def radiogroup_changed(self, e):
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.clear()
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
ft.Text(f"Sets the currency exchange rates in relation to {self.convert_currency_choice.value}"),
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'USD':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.euro,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.chf,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.gbp,
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'EURO':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.usd,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.chf,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.gbp,
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'CHF':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.usd,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.euro,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.gbp,
|
||||||
|
)
|
||||||
|
if self.convert_currency_choice.value == 'GBP':
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.usd,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.euro,
|
||||||
|
)
|
||||||
|
self.convert_courrency_dialog_placeholder.controls.append(
|
||||||
|
self.chf,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
fx = fetch_rates_for_base(self.convert_currency_choice.value)
|
||||||
|
self.euro.value = f"{fx['EURO']:.4f}"
|
||||||
|
self.usd.value = f"{fx['USD']:.4f}"
|
||||||
|
self.chf.value = f"{fx['CHF']:.4f}"
|
||||||
|
self.gbp.value = f"{fx['GBP']:.4f}"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.convert_courrency_dialog_placeholder.update()
|
||||||
|
|
||||||
|
def on_convert_curency_btn_click(self, e):
|
||||||
|
self.page.open(self.convert_courency_dialog)
|
||||||
|
base = self.convert_currency_choice.value or "EURO"
|
||||||
|
try:
|
||||||
|
fx = fetch_rates_for_base(base)
|
||||||
|
self.euro.value = f"{fx['EURO']:.4f}"
|
||||||
|
self.usd.value = f"{fx['USD']:.4f}"
|
||||||
|
self.chf.value = f"{fx['CHF']:.4f}"
|
||||||
|
self.gbp.value = f"{fx['GBP']:.4f}"
|
||||||
|
self.convert_courrency_dialog_placeholder.update()
|
||||||
|
except Exception as ex:
|
||||||
|
self.page.snack_bar = ft.SnackBar(ft.Text(f"FX fetch failed: {ex}"))
|
||||||
|
self.page.snack_bar.open = True
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.convert_courency_dialog)
|
||||||
|
|
||||||
|
def on_confirm_btn_click(self, e):
|
||||||
|
self.page.close(self.convert_courency_dialog)
|
||||||
|
# Build rates relative to the selected base currency.
|
||||||
|
# Example: if base is EURO, then rates['USD'] should be "1 EURO = X USD".
|
||||||
|
# To convert an amount from USD -> EURO we divide by rates['USD'].
|
||||||
|
base = self.convert_currency_choice.value
|
||||||
|
# Normalize and validate rate inputs
|
||||||
|
def _to_float(v):
|
||||||
|
try:
|
||||||
|
return float(v)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
rates = {
|
||||||
|
'EURO': _to_float(self.euro.value),
|
||||||
|
'USD': _to_float(self.usd.value),
|
||||||
|
'CHF': _to_float(self.chf.value),
|
||||||
|
'GBP': _to_float(self.gbp.value),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper to convert any amount to the selected base currency
|
||||||
|
def to_base(amount, currency):
|
||||||
|
try:
|
||||||
|
amt = float(amount)
|
||||||
|
except Exception:
|
||||||
|
return 0.0
|
||||||
|
if currency == base:
|
||||||
|
return amt
|
||||||
|
rate = rates.get(currency)
|
||||||
|
# If the rate is missing or invalid, keep original (safer than crashing)
|
||||||
|
if not rate or rate == 0:
|
||||||
|
return amt
|
||||||
|
# Convert from currency -> base by dividing when rates are "1 base = rate[currency] currency"
|
||||||
|
return amt / rate
|
||||||
|
|
||||||
|
# Rebuild table in base currency
|
||||||
|
self.rows_copy = list(self.rows)
|
||||||
|
total = 0.0
|
||||||
|
self.data_table.rows.clear()
|
||||||
|
|
||||||
|
for r in self.rows_copy:
|
||||||
|
# r structure: [order_number, client_name, transporter_name, order_date, paid, received, profit, currency]
|
||||||
|
original_currency = r[7]
|
||||||
|
|
||||||
|
paid_base = to_base(r[4], original_currency)
|
||||||
|
received_base = to_base(r[5], original_currency)
|
||||||
|
profit_base = round(float(received_base) - float(paid_base), 2)
|
||||||
|
|
||||||
|
# Update a display row (do not mutate self.rows source amounts)
|
||||||
|
row = ft.DataRow(
|
||||||
|
cells=[
|
||||||
|
ft.DataCell(ft.Text(r[0])),
|
||||||
|
ft.DataCell(ft.Text(r[1])),
|
||||||
|
ft.DataCell(ft.Text(r[2])),
|
||||||
|
ft.DataCell(ft.Text(r[3])),
|
||||||
|
ft.DataCell(ft.Text(f"{paid_base:.2f}")),
|
||||||
|
ft.DataCell(ft.Text(f"{received_base:.2f}")),
|
||||||
|
ft.DataCell(ft.Text(f"{profit_base:.2f}")),
|
||||||
|
ft.DataCell(ft.Text(base)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.data_table.rows.append(row)
|
||||||
|
total += profit_base
|
||||||
|
|
||||||
|
self.data_table.update()
|
||||||
|
self.total.value = f"Total: {total:.2f} {base}"
|
||||||
|
self.total.update()
|
||||||
|
|
||||||
def get_orders(self):
|
def get_orders(self):
|
||||||
try:
|
try:
|
||||||
token = self.page.client_storage.get("token")
|
token = self.page.client_storage.get("token")
|
||||||
@@ -106,6 +312,7 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(r[4])),
|
ft.DataCell(ft.Text(r[4])),
|
||||||
ft.DataCell(ft.Text(r[5])),
|
ft.DataCell(ft.Text(r[5])),
|
||||||
ft.DataCell(ft.Text(r[6])),
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
ft.DataCell(ft.Text(r[7])),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
@@ -113,13 +320,17 @@ class ReportPage:
|
|||||||
total += r[6]
|
total += r[6]
|
||||||
self.rows_copy = buffer
|
self.rows_copy = buffer
|
||||||
self.data_table.update()
|
self.data_table.update()
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.total.update()
|
self.total.update()
|
||||||
|
|
||||||
def create_table_rows_data(self):
|
def create_table_rows_data(self):
|
||||||
all_orders = self.get_orders()
|
all_orders = self.get_orders()
|
||||||
total = 0
|
total = 0
|
||||||
for order in all_orders:
|
for order in all_orders:
|
||||||
|
if order['currency'] not in self.currency_list:
|
||||||
|
self.currency_list.append(order['currency'])
|
||||||
|
if len(self.currency_list) > 1:
|
||||||
|
self.convert_currency_placeholder.content=self.convert_currency
|
||||||
# Skip non-active orders from reports
|
# Skip non-active orders from reports
|
||||||
#print(order.get('status'))
|
#print(order.get('status'))
|
||||||
if order.get('status') != 'active':
|
if order.get('status') != 'active':
|
||||||
@@ -130,6 +341,7 @@ class ReportPage:
|
|||||||
order_date = order['created_at'].split("T")[0]
|
order_date = order['created_at'].split("T")[0]
|
||||||
paid = order['paid_price']
|
paid = order['paid_price']
|
||||||
received = order['received_price']
|
received = order['received_price']
|
||||||
|
currency = order['currency']
|
||||||
try:
|
try:
|
||||||
profit = round(float(received) - float(paid), 2)
|
profit = round(float(received) - float(paid), 2)
|
||||||
except:
|
except:
|
||||||
@@ -144,13 +356,14 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(paid)),
|
ft.DataCell(ft.Text(paid)),
|
||||||
ft.DataCell(ft.Text(received)),
|
ft.DataCell(ft.Text(received)),
|
||||||
ft.DataCell(ft.Text(profit)),
|
ft.DataCell(ft.Text(profit)),
|
||||||
|
ft.DataCell(ft.Text(currency)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
row_data = [order_number, client_name, transporter_name, order_date, paid, received, profit]
|
row_data = [order_number, client_name, transporter_name, order_date, paid, received, profit, currency]
|
||||||
self.rows.append(row_data)
|
self.rows.append(row_data)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
total += profit
|
total += profit
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.rows_copy = self.rows
|
self.rows_copy = self.rows
|
||||||
|
|
||||||
def on_reset_btn_click(self, e):
|
def on_reset_btn_click(self, e):
|
||||||
@@ -202,12 +415,13 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(r[4])),
|
ft.DataCell(ft.Text(r[4])),
|
||||||
ft.DataCell(ft.Text(r[5])),
|
ft.DataCell(ft.Text(r[5])),
|
||||||
ft.DataCell(ft.Text(r[6])),
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
ft.DataCell(ft.Text(r[7])),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
total += r[6]
|
total += r[6]
|
||||||
self.data_table.update()
|
self.data_table.update()
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.total.update()
|
self.total.update()
|
||||||
self.start_date.value = ""
|
self.start_date.value = ""
|
||||||
self.start_date.update()
|
self.start_date.update()
|
||||||
@@ -233,6 +447,7 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(r[4])),
|
ft.DataCell(ft.Text(r[4])),
|
||||||
ft.DataCell(ft.Text(r[5])),
|
ft.DataCell(ft.Text(r[5])),
|
||||||
ft.DataCell(ft.Text(r[6])),
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
ft.DataCell(ft.Text(r[7])),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
@@ -240,7 +455,7 @@ class ReportPage:
|
|||||||
total += r[6]
|
total += r[6]
|
||||||
self.rows_copy = buffer
|
self.rows_copy = buffer
|
||||||
self.data_table.update()
|
self.data_table.update()
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.total.update()
|
self.total.update()
|
||||||
|
|
||||||
def on_end_date_click(self, e):
|
def on_end_date_click(self, e):
|
||||||
@@ -262,6 +477,7 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(r[4])),
|
ft.DataCell(ft.Text(r[4])),
|
||||||
ft.DataCell(ft.Text(r[5])),
|
ft.DataCell(ft.Text(r[5])),
|
||||||
ft.DataCell(ft.Text(r[6])),
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
ft.DataCell(ft.Text(r[7])),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
@@ -269,7 +485,7 @@ class ReportPage:
|
|||||||
total += r[6]
|
total += r[6]
|
||||||
self.rows_copy = buffer
|
self.rows_copy = buffer
|
||||||
self.data_table.update()
|
self.data_table.update()
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.total.update()
|
self.total.update()
|
||||||
|
|
||||||
def filter_by_client(self, e):
|
def filter_by_client(self, e):
|
||||||
@@ -289,6 +505,7 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(r[4])),
|
ft.DataCell(ft.Text(r[4])),
|
||||||
ft.DataCell(ft.Text(r[5])),
|
ft.DataCell(ft.Text(r[5])),
|
||||||
ft.DataCell(ft.Text(r[6])),
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
ft.DataCell(ft.Text(r[7])),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
@@ -296,7 +513,7 @@ class ReportPage:
|
|||||||
total += r[6]
|
total += r[6]
|
||||||
self.rows_copy = buffer
|
self.rows_copy = buffer
|
||||||
self.data_table.update()
|
self.data_table.update()
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.total.update()
|
self.total.update()
|
||||||
|
|
||||||
def filter_by_transporter(self, e):
|
def filter_by_transporter(self, e):
|
||||||
@@ -316,6 +533,7 @@ class ReportPage:
|
|||||||
ft.DataCell(ft.Text(r[4])),
|
ft.DataCell(ft.Text(r[4])),
|
||||||
ft.DataCell(ft.Text(r[5])),
|
ft.DataCell(ft.Text(r[5])),
|
||||||
ft.DataCell(ft.Text(r[6])),
|
ft.DataCell(ft.Text(r[6])),
|
||||||
|
ft.DataCell(ft.Text(r[7])),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.data_table.rows.append(row)
|
self.data_table.rows.append(row)
|
||||||
@@ -323,7 +541,7 @@ class ReportPage:
|
|||||||
total += r[6]
|
total += r[6]
|
||||||
self.rows_copy = buffer
|
self.rows_copy = buffer
|
||||||
self.data_table.update()
|
self.data_table.update()
|
||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}" if len(self.currency_list)==1 else ""
|
||||||
self.total.update()
|
self.total.update()
|
||||||
|
|
||||||
def get_client_access(self):
|
def get_client_access(self):
|
||||||
@@ -344,41 +562,47 @@ class ReportPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Reports", size=24, weight=ft.FontWeight.BOLD),
|
ft.Text("Report Orders Out", size=24, weight=ft.FontWeight.BOLD),
|
||||||
self.total
|
self.total
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
),
|
),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
self.start_date,
|
ft.Row(
|
||||||
ft.ElevatedButton(
|
[
|
||||||
"Start Date",
|
self.start_date,
|
||||||
on_click=lambda _: self.page.open(ft.DatePicker(
|
ft.ElevatedButton(
|
||||||
first_date=datetime(year=2000, month=10, day=1),
|
"Start Date",
|
||||||
last_date=datetime(year=2025, month=10, day=1),
|
on_click=lambda _: self.page.open(ft.DatePicker(
|
||||||
on_change=self.on_start_date_click,
|
first_date=datetime(year=2000, month=10, day=1),
|
||||||
)),
|
last_date=datetime(year=2025, month=10, day=1),
|
||||||
width=120,
|
on_change=self.on_start_date_click,
|
||||||
icon=ft.Icons.CALENDAR_MONTH
|
)),
|
||||||
),
|
width=120,
|
||||||
self.end_date,
|
icon=ft.Icons.CALENDAR_MONTH
|
||||||
ft.ElevatedButton(
|
),
|
||||||
"End Date",
|
self.end_date,
|
||||||
on_click=lambda _: self.page.open(ft.DatePicker(
|
ft.ElevatedButton(
|
||||||
first_date=datetime(year=2000, month=10, day=1),
|
"End Date",
|
||||||
last_date=datetime(year=2025, month=10, day=1),
|
on_click=lambda _: self.page.open(ft.DatePicker(
|
||||||
on_change=self.on_end_date_click,
|
first_date=datetime(year=2000, month=10, day=1),
|
||||||
)),
|
last_date=datetime(year=2025, month=10, day=1),
|
||||||
width=120,
|
on_change=self.on_end_date_click,
|
||||||
icon=ft.Icons.CALENDAR_MONTH
|
)),
|
||||||
),
|
width=120,
|
||||||
ft.Text(),
|
icon=ft.Icons.CALENDAR_MONTH
|
||||||
self.clients_filter_placeholder,
|
),
|
||||||
self.transporters_filter_placeholder,
|
ft.Text(),
|
||||||
ft.ElevatedButton("Reset", on_click=self.on_reset_btn_click, width=120),
|
self.clients_filter_placeholder,
|
||||||
]
|
self.transporters_filter_placeholder,
|
||||||
),
|
ft.ElevatedButton("Reset", on_click=self.on_reset_btn_click, width=120),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
self.convert_currency_placeholder
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
ft.Column(
|
ft.Column(
|
||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
@@ -403,7 +627,7 @@ class ReportPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Reports", size=24, weight=ft.FontWeight.BOLD),
|
ft.Text("Reports Order Out", size=24, weight=ft.FontWeight.BOLD),
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
),
|
),
|
||||||
110
transportmanager/client/pages/reports_page.py
Normal file
110
transportmanager/client/pages/reports_page.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import flet as ft
|
||||||
|
from pages.report_order_out_page import ReportOrderOutPage
|
||||||
|
from pages.report_order_in_page import ReportOrderInPage
|
||||||
|
|
||||||
|
class ReportsPage:
|
||||||
|
def __init__(self, page: ft.Page, dashboard):
|
||||||
|
self.page = page
|
||||||
|
self.dashboard = dashboard
|
||||||
|
|
||||||
|
def on_report_orders_in_btn_click(self, e):
|
||||||
|
order_in_page = ReportOrderInPage(self.page, self.dashboard)
|
||||||
|
self.dashboard.placeholder.content = order_in_page.build()
|
||||||
|
self.dashboard.placeholder.update()
|
||||||
|
|
||||||
|
def on_report_orders_out_btn_click(self, e):
|
||||||
|
orders_out_page = ReportOrderOutPage(self.page, self.dashboard)
|
||||||
|
self.dashboard.placeholder.content = orders_out_page.build()
|
||||||
|
self.dashboard.placeholder.update()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("Reports", size=24, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.ASSESSMENT, size=150),
|
||||||
|
ft.Container(
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Report for Orders In", size=20)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
bgcolor=ft.Colors.BLUE_200,
|
||||||
|
width=250,
|
||||||
|
height=80
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Report",
|
||||||
|
on_click=self.on_report_orders_in_btn_click,
|
||||||
|
width=100
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_EVENLY
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
border=ft.border.all(1, ft.Colors.GREY_300),
|
||||||
|
bgcolor=ft.Colors.BLUE_50,
|
||||||
|
padding = ft.padding.symmetric(vertical=20),
|
||||||
|
width=250,
|
||||||
|
height=350,
|
||||||
|
border_radius=20
|
||||||
|
),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.ASSESSMENT, size=150),
|
||||||
|
ft.Container(
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Report for Orders Out", size=20)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
bgcolor=ft.Colors.BLUE_200,
|
||||||
|
width=250,
|
||||||
|
height=80
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Report",
|
||||||
|
on_click=self.on_report_orders_out_btn_click,
|
||||||
|
width=100
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_EVENLY
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
border=ft.border.all(1, ft.Colors.GREY_300),
|
||||||
|
bgcolor=ft.Colors.BLUE_50,
|
||||||
|
padding = ft.padding.symmetric(vertical=20),
|
||||||
|
width=250,
|
||||||
|
height=350,
|
||||||
|
border_radius=20
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
spacing=50
|
||||||
|
),
|
||||||
|
ft.Text(" ")
|
||||||
|
],
|
||||||
|
expand=True,
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
@@ -298,6 +298,31 @@ class ViewOrdersIn:
|
|||||||
|
|
||||||
self.filename = ft.Text(value=self.order['file'])
|
self.filename = ft.Text(value=self.order['file'])
|
||||||
|
|
||||||
|
self.currency = ft.Dropdown(
|
||||||
|
editable=True,
|
||||||
|
label="Currency",
|
||||||
|
options=self.get_currency(),
|
||||||
|
value=self.order['currency'],
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_currency(self):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/currency/", headers=headers)
|
||||||
|
currency_list = response.json() if response.status_code == 200 else []
|
||||||
|
|
||||||
|
options = []
|
||||||
|
for currency in currency_list:
|
||||||
|
options.append(
|
||||||
|
ft.DropdownOption(
|
||||||
|
key=currency['name'],
|
||||||
|
content=ft.Text(
|
||||||
|
value=currency['name'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return options
|
||||||
|
|
||||||
def on_go_back_btn_click(self, e):
|
def on_go_back_btn_click(self, e):
|
||||||
self.dashboard.placeholder.content = self.archive.build()
|
self.dashboard.placeholder.content = self.archive.build()
|
||||||
self.dashboard.placeholder.update()
|
self.dashboard.placeholder.update()
|
||||||
@@ -826,6 +851,7 @@ class ViewOrdersIn:
|
|||||||
'unloading_addresses': unloading_addresses,
|
'unloading_addresses': unloading_addresses,
|
||||||
'file':self.filename.value,
|
'file':self.filename.value,
|
||||||
'expenses': self.expenses.value,
|
'expenses': self.expenses.value,
|
||||||
|
'currency': self.currency.value
|
||||||
}
|
}
|
||||||
#print(saved_data)
|
#print(saved_data)
|
||||||
if self.order_number.value == None or len(self.order_number.value)==0:
|
if self.order_number.value == None or len(self.order_number.value)==0:
|
||||||
@@ -1038,7 +1064,8 @@ class ViewOrdersIn:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD)
|
ft.Text("Price / Expenses", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
self.currency
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.START
|
alignment=ft.MainAxisAlignment.START
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ from routes.transporters import transporters_bp
|
|||||||
from routes.destinations import destinations_bp
|
from routes.destinations import destinations_bp
|
||||||
from routes.orders_out import orders_bp
|
from routes.orders_out import orders_bp
|
||||||
from routes.ouders_in import orders_in_bp
|
from routes.ouders_in import orders_in_bp
|
||||||
from routes.report import report_bp
|
from routes.report_order_out import report_order_out_bp
|
||||||
from admin.subscription import admin_subscription_bp
|
from admin.subscription import admin_subscription_bp
|
||||||
from routes.subscription import subscription_bp
|
from routes.subscription import subscription_bp
|
||||||
from admin.tenants import admin_user_bp
|
from admin.tenants import admin_user_bp
|
||||||
from routes.company_user import company_user_bp
|
from routes.company_user import company_user_bp
|
||||||
|
from routes.currency import currency_bp
|
||||||
|
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from models.subscription import Subscription
|
from models.subscription import Subscription
|
||||||
@@ -66,11 +67,12 @@ app.register_blueprint(transporters_bp, url_prefix="/transporters")
|
|||||||
app.register_blueprint(destinations_bp, url_prefix="/destinations")
|
app.register_blueprint(destinations_bp, url_prefix="/destinations")
|
||||||
app.register_blueprint(orders_bp, url_prefix="/orders")
|
app.register_blueprint(orders_bp, url_prefix="/orders")
|
||||||
app.register_blueprint(orders_in_bp, url_prefix="/orders_in")
|
app.register_blueprint(orders_in_bp, url_prefix="/orders_in")
|
||||||
app.register_blueprint(report_bp, url_prefix="/report")
|
app.register_blueprint(report_order_out_bp)
|
||||||
app.register_blueprint(admin_subscription_bp)
|
app.register_blueprint(admin_subscription_bp)
|
||||||
app.register_blueprint(subscription_bp)
|
app.register_blueprint(subscription_bp)
|
||||||
app.register_blueprint(admin_user_bp)
|
app.register_blueprint(admin_user_bp)
|
||||||
app.register_blueprint(company_user_bp)
|
app.register_blueprint(company_user_bp)
|
||||||
|
app.register_blueprint(currency_bp)
|
||||||
|
|
||||||
def update_subscription_statuses_job():
|
def update_subscription_statuses_job():
|
||||||
print("[Scheduler] Running daily subscription status check...")
|
print("[Scheduler] Running daily subscription status check...")
|
||||||
|
|||||||
Binary file not shown.
53
transportmanager/server/models/currency.py
Normal file
53
transportmanager/server/models/currency.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from database import get_connection, is_postgres
|
||||||
|
|
||||||
|
class Currency:
|
||||||
|
def __init__(self):
|
||||||
|
self.ph = "%s" if is_postgres() else "?"
|
||||||
|
|
||||||
|
def currency_to_dict(self, row):
|
||||||
|
currency = {
|
||||||
|
'id': row[0],
|
||||||
|
'user_id': row[1],
|
||||||
|
'name': row[2],
|
||||||
|
'value': row[3],
|
||||||
|
'created_at': row[4]
|
||||||
|
}
|
||||||
|
return currency
|
||||||
|
|
||||||
|
def insert_currency(self, access_data):
|
||||||
|
created_at = datetime.now().isoformat()
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
returning = "RETURNING id" if is_postgres() else ""
|
||||||
|
query = f"""
|
||||||
|
INSERT INTO currency (
|
||||||
|
user_id, name, value, created_at
|
||||||
|
) VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}) {returning}
|
||||||
|
"""
|
||||||
|
cursor.execute(
|
||||||
|
query,
|
||||||
|
(
|
||||||
|
access_data['user_id'],
|
||||||
|
access_data['name'],
|
||||||
|
access_data['value'],
|
||||||
|
created_at
|
||||||
|
)
|
||||||
|
)
|
||||||
|
inserted_id = None
|
||||||
|
if is_postgres():
|
||||||
|
inserted_id = cursor.fetchone()[0]
|
||||||
|
else:
|
||||||
|
inserted_id = cursor.lastrowid
|
||||||
|
if hasattr(conn, "commit"):
|
||||||
|
conn.commit()
|
||||||
|
return inserted_id
|
||||||
|
|
||||||
|
def get_currency_user_id(self, user_id):
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(f"SELECT * FROM currency WHERE user_id = {self.ph}", (user_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return self.currency_to_dict(row) if row else None
|
||||||
|
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ class OrdersIn:
|
|||||||
"created_at": row[10],
|
"created_at": row[10],
|
||||||
"file":row[11],
|
"file":row[11],
|
||||||
"expenses": row[12],
|
"expenses": row[12],
|
||||||
|
"currency": row[13]
|
||||||
}
|
}
|
||||||
|
|
||||||
def order_point_to_dict(self, row):
|
def order_point_to_dict(self, row):
|
||||||
@@ -43,8 +44,8 @@ class OrdersIn:
|
|||||||
f"""
|
f"""
|
||||||
INSERT INTO orders_in
|
INSERT INTO orders_in
|
||||||
(user_id, client_id, products_description, received_price, order_number,
|
(user_id, client_id, products_description, received_price, order_number,
|
||||||
ldb_quantity, kg_quantity, track_reg_number, trailer_reg_number, created_at, file_name, expenses)
|
ldb_quantity, kg_quantity, track_reg_number, trailer_reg_number, created_at, file_name, expenses, currency)
|
||||||
VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}){returning}
|
VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}){returning}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
data["user_id"],
|
data["user_id"],
|
||||||
@@ -58,7 +59,8 @@ class OrdersIn:
|
|||||||
data["trailer_reg_number"],
|
data["trailer_reg_number"],
|
||||||
created_at,
|
created_at,
|
||||||
data["file"],
|
data["file"],
|
||||||
data["expenses"]
|
data["expenses"],
|
||||||
|
data['currency']
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
new_id = cur.fetchone()[0] if is_postgres() else getattr(cur, "lastrowid", None)
|
new_id = cur.fetchone()[0] if is_postgres() else getattr(cur, "lastrowid", None)
|
||||||
@@ -75,7 +77,7 @@ class OrdersIn:
|
|||||||
user_id = {self.ph}, client_id = {self.ph}, products_description = {self.ph},
|
user_id = {self.ph}, client_id = {self.ph}, products_description = {self.ph},
|
||||||
received_price = {self.ph}, order_number = {self.ph},
|
received_price = {self.ph}, order_number = {self.ph},
|
||||||
ldb_quantity = {self.ph}, kg_quantity = {self.ph}, track_reg_number = {self.ph},
|
ldb_quantity = {self.ph}, kg_quantity = {self.ph}, track_reg_number = {self.ph},
|
||||||
trailer_reg_number = {self.ph}, file_name = {self.ph}, expenses = {self.ph}
|
trailer_reg_number = {self.ph}, file_name = {self.ph}, expenses = {self.ph}, currency = {self.ph}
|
||||||
WHERE id = {self.ph}
|
WHERE id = {self.ph}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
@@ -90,6 +92,7 @@ class OrdersIn:
|
|||||||
data["trailer_reg_number"],
|
data["trailer_reg_number"],
|
||||||
data['file'],
|
data['file'],
|
||||||
data['expenses'],
|
data['expenses'],
|
||||||
|
data['currency'],
|
||||||
data["id"],
|
data["id"],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ class OrdersOut:
|
|||||||
"received_price": row[10],
|
"received_price": row[10],
|
||||||
"paid_price": row[11],
|
"paid_price": row[11],
|
||||||
"created_at": row[12],
|
"created_at": row[12],
|
||||||
"status": row[13]
|
"status": row[13],
|
||||||
|
"order_in_number": row[14],
|
||||||
|
"currency":row[15]
|
||||||
}
|
}
|
||||||
|
|
||||||
def order_point_to_dict(self, row):
|
def order_point_to_dict(self, row):
|
||||||
@@ -44,8 +46,8 @@ class OrdersOut:
|
|||||||
f"""
|
f"""
|
||||||
INSERT INTO orders_out
|
INSERT INTO orders_out
|
||||||
(user_id, client_id, transporter_id, products_description, received_price, paid_price, order_number,
|
(user_id, client_id, transporter_id, products_description, received_price, paid_price, order_number,
|
||||||
ldb_quantity, kg_quantity, track_reg_number, trailer_reg_number, created_at)
|
ldb_quantity, kg_quantity, track_reg_number, trailer_reg_number, created_at, order_in_number, currency)
|
||||||
VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}){returning}
|
VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}){returning}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
data["user_id"],
|
data["user_id"],
|
||||||
@@ -60,6 +62,8 @@ class OrdersOut:
|
|||||||
data["track_reg_number"],
|
data["track_reg_number"],
|
||||||
data["trailer_reg_number"],
|
data["trailer_reg_number"],
|
||||||
created_at,
|
created_at,
|
||||||
|
data['order_in_number'],
|
||||||
|
data['currency']
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
new_id = cursor.fetchone()[0] if is_postgres() else getattr(cursor, "lastrowid", None)
|
new_id = cursor.fetchone()[0] if is_postgres() else getattr(cursor, "lastrowid", None)
|
||||||
@@ -75,7 +79,7 @@ class OrdersOut:
|
|||||||
UPDATE orders_out SET client_id = {self.ph}, transporter_id = {self.ph}, products_description = {self.ph},
|
UPDATE orders_out SET client_id = {self.ph}, transporter_id = {self.ph}, products_description = {self.ph},
|
||||||
received_price = {self.ph}, paid_price = {self.ph}, order_number = {self.ph},
|
received_price = {self.ph}, paid_price = {self.ph}, order_number = {self.ph},
|
||||||
ldb_quantity = {self.ph}, kg_quantity = {self.ph}, track_reg_number = {self.ph},
|
ldb_quantity = {self.ph}, kg_quantity = {self.ph}, track_reg_number = {self.ph},
|
||||||
trailer_reg_number = {self.ph}
|
trailer_reg_number = {self.ph}, order_in_number = {self.ph}, currency = {self.ph}
|
||||||
WHERE id = {self.ph}
|
WHERE id = {self.ph}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
@@ -89,6 +93,8 @@ class OrdersOut:
|
|||||||
data["kg_quantity"],
|
data["kg_quantity"],
|
||||||
data["track_reg_number"],
|
data["track_reg_number"],
|
||||||
data["trailer_reg_number"],
|
data["trailer_reg_number"],
|
||||||
|
data['order_in_number'],
|
||||||
|
data['currency'],
|
||||||
data["id"],
|
data["id"],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ class Users:
|
|||||||
returning = "RETURNING id" if is_postgres() else ""
|
returning = "RETURNING id" if is_postgres() else ""
|
||||||
query = f"""
|
query = f"""
|
||||||
INSERT INTO users (
|
INSERT INTO users (
|
||||||
name, email, password_hash, created_at, user_role, company_id
|
name, email, password_hash, created_at, user_role, company_id, temporary_password
|
||||||
) VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}) {returning}
|
) VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}) {returning}
|
||||||
"""
|
"""
|
||||||
cursor.execute(query, (name, email, password_hash, created_at, user_role, company_id))
|
cursor.execute(query, (name, email, password_hash, created_at, user_role, company_id, 1))
|
||||||
inserted_id = None
|
inserted_id = None
|
||||||
if is_postgres():
|
if is_postgres():
|
||||||
inserted_id = cursor.fetchone()[0]
|
inserted_id = cursor.fetchone()[0]
|
||||||
|
|||||||
36
transportmanager/server/routes/currency.py
Normal file
36
transportmanager/server/routes/currency.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from models.currency import Currency
|
||||||
|
from models.user import Users
|
||||||
|
|
||||||
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
|
|
||||||
|
currency_bp = Blueprint("currency", __name__, url_prefix="/currency")
|
||||||
|
|
||||||
|
@currency_bp.route("/", methods=["GET"])
|
||||||
|
@jwt_required()
|
||||||
|
def list_currency():
|
||||||
|
#currency = Currency()
|
||||||
|
currency = [
|
||||||
|
{
|
||||||
|
'id':1,
|
||||||
|
'name':'USD',
|
||||||
|
'value':'',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id':2,
|
||||||
|
'name':'EURO',
|
||||||
|
'value':'',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id':3,
|
||||||
|
'name':'CHF',
|
||||||
|
'value':'',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id':4,
|
||||||
|
'name':'GBP',
|
||||||
|
'value':'',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return jsonify(currency), 200
|
||||||
@@ -50,6 +50,8 @@ def create_order_route():
|
|||||||
'track_reg_number': incoming_data["track_reg_number"],
|
'track_reg_number': incoming_data["track_reg_number"],
|
||||||
'trailer_reg_number': incoming_data["trailer_reg_number"],
|
'trailer_reg_number': incoming_data["trailer_reg_number"],
|
||||||
'products_description': incoming_data["products_description"],
|
'products_description': incoming_data["products_description"],
|
||||||
|
'order_in_number': incoming_data["order_in_number"],
|
||||||
|
'currency': incoming_data["currency"]
|
||||||
}
|
}
|
||||||
order_id = orders.create_order(order_data)
|
order_id = orders.create_order(order_data)
|
||||||
|
|
||||||
@@ -110,6 +112,8 @@ def update_order_route(order_id):
|
|||||||
"track_reg_number": data.get("track_reg_number", order["track_reg_number"]),
|
"track_reg_number": data.get("track_reg_number", order["track_reg_number"]),
|
||||||
"trailer_reg_number": data.get("trailer_reg_number", order["trailer_reg_number"]),
|
"trailer_reg_number": data.get("trailer_reg_number", order["trailer_reg_number"]),
|
||||||
"products_description": data.get("products_description", order["products_description"]),
|
"products_description": data.get("products_description", order["products_description"]),
|
||||||
|
"order_in_number": data.get("order_in_number", order["order_in_number"]),
|
||||||
|
"currency":data.get("currency", order["currency"]),
|
||||||
})
|
})
|
||||||
|
|
||||||
orders.delete_points_by_order_id(order_id)
|
orders.delete_points_by_order_id(order_id)
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ def create_order_in_route():
|
|||||||
'trailer_reg_number': incoming_data["trailer_reg_number"],
|
'trailer_reg_number': incoming_data["trailer_reg_number"],
|
||||||
'products_description': incoming_data["products_description"],
|
'products_description': incoming_data["products_description"],
|
||||||
'file': incoming_data['file'],
|
'file': incoming_data['file'],
|
||||||
'expenses': incoming_data['expenses']
|
'expenses': incoming_data['expenses'],
|
||||||
|
'currency': incoming_data['currency']
|
||||||
}
|
}
|
||||||
#print(order_data)
|
#print(order_data)
|
||||||
order_id = orders.create_order(order_data)
|
order_id = orders.create_order(order_data)
|
||||||
@@ -91,9 +92,10 @@ def update_order_route(order_id):
|
|||||||
"track_reg_number": data.get("track_reg_number", order["track_reg_number"]),
|
"track_reg_number": data.get("track_reg_number", order["track_reg_number"]),
|
||||||
"trailer_reg_number": data.get("trailer_reg_number", order["trailer_reg_number"]),
|
"trailer_reg_number": data.get("trailer_reg_number", order["trailer_reg_number"]),
|
||||||
"products_description": data.get("products_description", order["products_description"]),
|
"products_description": data.get("products_description", order["products_description"]),
|
||||||
'file': data.get("file", order["file"]),
|
"file": data.get("file", order["file"]),
|
||||||
'expenses': data.get("expenses", order["expenses"]),
|
"expenses": data.get("expenses", order["expenses"]),
|
||||||
"user_id":user_id
|
"currency": data.get("currency", order["currency"]),
|
||||||
|
"user_id":user_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
orders.delete_points_by_order_id(order_id)
|
orders.delete_points_by_order_id(order_id)
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ def get_profile():
|
|||||||
"terms": user["terms"],
|
"terms": user["terms"],
|
||||||
"first_order_number": user["first_order_number"],
|
"first_order_number": user["first_order_number"],
|
||||||
"user_role": user["user_role"],
|
"user_role": user["user_role"],
|
||||||
"vat":user["vat"]
|
"vat":user["vat"],
|
||||||
|
"company_id":user['company_id']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ from models.order_out import OrdersOut # Your plain SQL model
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from models.user import Users
|
from models.user import Users
|
||||||
|
|
||||||
report_bp = Blueprint("report", __name__, url_prefix="/report")
|
report_order_out_bp = Blueprint("report_order_out", __name__, url_prefix="/report_order_out")
|
||||||
|
|
||||||
@report_bp.route("/profit", methods=["GET"])
|
@report_order_out_bp.route("/profit", methods=["GET"])
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
def get_profit_report():
|
def get_profit_report():
|
||||||
try:
|
try:
|
||||||
@@ -1,4 +1,26 @@
|
|||||||
|
-- Reset schema: drop tables first (children → parents), then recreate.
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
|
||||||
|
-- Drop child tables first to avoid FK constraints
|
||||||
|
DROP TABLE IF EXISTS order_in_points;
|
||||||
|
DROP TABLE IF EXISTS order_out_points;
|
||||||
|
DROP TABLE IF EXISTS orders_in;
|
||||||
|
DROP TABLE IF EXISTS orders_out;
|
||||||
|
DROP TABLE IF EXISTS email;
|
||||||
|
DROP TABLE IF EXISTS subscriptions;
|
||||||
|
DROP TABLE IF EXISTS company_user_access;
|
||||||
|
DROP TABLE IF EXISTS destinations;
|
||||||
|
DROP TABLE IF EXISTS transporters;
|
||||||
|
DROP TABLE IF EXISTS clients;
|
||||||
|
DROP TABLE IF EXISTS currency;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
-- Users table
|
-- Users table
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
@@ -18,7 +40,7 @@ CREATE TABLE IF NOT EXISTS users (
|
|||||||
user_role TEXT NOT NULL DEFAULT 'user' CHECK (user_role IN ('user', 'admin', 'company_user')),
|
user_role TEXT NOT NULL DEFAULT 'user' CHECK (user_role IN ('user', 'admin', 'company_user')),
|
||||||
company_id INTEGER DEFAULT 0,
|
company_id INTEGER DEFAULT 0,
|
||||||
active INTEGER DEFAULT 1,
|
active INTEGER DEFAULT 1,
|
||||||
temporary_passwrd INTEGER DEFAULT 0
|
temporary_password INTEGER DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Clients table
|
-- Clients table
|
||||||
@@ -79,6 +101,8 @@ CREATE TABLE IF NOT EXISTS orders_out (
|
|||||||
paid_price DOUBLE PRECISION,
|
paid_price DOUBLE PRECISION,
|
||||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
order_status TEXT NOT NULL DEFAULT 'active' CHECK (order_status IN ('active', 'inactive', 'cancelled')),
|
order_status TEXT NOT NULL DEFAULT 'active' CHECK (order_status IN ('active', 'inactive', 'cancelled')),
|
||||||
|
order_in_number TEXT,
|
||||||
|
currency TEXT,
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY(client_id) REFERENCES clients(id) ON DELETE CASCADE,
|
FOREIGN KEY(client_id) REFERENCES clients(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY(transporter_id) REFERENCES transporters(id) ON DELETE CASCADE
|
FOREIGN KEY(transporter_id) REFERENCES transporters(id) ON DELETE CASCADE
|
||||||
@@ -99,6 +123,7 @@ CREATE TABLE IF NOT EXISTS orders_in (
|
|||||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
file_name TEXT,
|
file_name TEXT,
|
||||||
expenses DOUBLE PRECISION,
|
expenses DOUBLE PRECISION,
|
||||||
|
currency TEXT,
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY(client_id) REFERENCES clients(id) ON DELETE CASCADE
|
FOREIGN KEY(client_id) REFERENCES clients(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
@@ -162,3 +187,12 @@ CREATE TABLE IF NOT EXISTS company_user_access (
|
|||||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY(company_user_id) REFERENCES users(id) ON DELETE CASCADE
|
FOREIGN KEY(company_user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS currency (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
name TEXT,
|
||||||
|
value DOUBLE PRECISION,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
@@ -191,9 +191,13 @@ def generate_order_pdf(order, user_data, transporter_data, logo_path, save_to_di
|
|||||||
|
|
||||||
doc.build(elements)
|
doc.build(elements)
|
||||||
buffer.seek(0)
|
buffer.seek(0)
|
||||||
|
user_id = order['user_id']
|
||||||
|
if user_data['user_role'] == 'company_user':
|
||||||
|
user_id = user_data['company_id']
|
||||||
if save_to_disk:
|
if save_to_disk:
|
||||||
save_path=f"generated_pdfs/order_{order['user_id']}_{order['order_number']}.pdf"
|
save_path=f"generated_pdfs/order_{user_id}_{order['order_number']}.pdf"
|
||||||
with open(save_path, "wb") as f:
|
with open(save_path, "wb") as f:
|
||||||
f.write(buffer.getvalue())
|
f.write(buffer.getvalue())
|
||||||
|
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user