intrgrating suggestions after betta 1
This commit is contained in:
@@ -58,6 +58,11 @@ class ArchiveInPage:
|
|||||||
self.selected_delete_order = order
|
self.selected_delete_order = order
|
||||||
self.page.open(self.delete_dialog)
|
self.page.open(self.delete_dialog)
|
||||||
|
|
||||||
|
def download_order_file(self, order):
|
||||||
|
file = order['file']
|
||||||
|
url=f"{API_BASE_URL}/orders_in/files/{file}"
|
||||||
|
self.page.launch_url(url)
|
||||||
|
|
||||||
def load_orders(self):
|
def load_orders(self):
|
||||||
try:
|
try:
|
||||||
token = self.page.client_storage.get("token")
|
token = self.page.client_storage.get("token")
|
||||||
@@ -90,6 +95,11 @@ class ArchiveInPage:
|
|||||||
self.orders_list.controls.clear()
|
self.orders_list.controls.clear()
|
||||||
for order in self.orders:
|
for order in self.orders:
|
||||||
client = self.get_client(order['client_id'])
|
client = self.get_client(order['client_id'])
|
||||||
|
buttons = []
|
||||||
|
if order.get("file"):
|
||||||
|
buttons.append(ft.Button("Download", icon=ft.Icons.DOWNLOAD, on_click=lambda e, o=order: self.download_order_file(o)))
|
||||||
|
buttons.append(ft.Button("View", icon=ft.Icons.PICTURE_AS_PDF, on_click=lambda e, o=order: self.view_order(o)))
|
||||||
|
buttons.append(ft.Button("Delete", icon=ft.Icons.CANCEL, on_click=lambda e, o=order: self.cancel_order(o)))
|
||||||
self.orders_list.controls.append(
|
self.orders_list.controls.append(
|
||||||
ft.Container(
|
ft.Container(
|
||||||
content=ft.Row([
|
content=ft.Row([
|
||||||
@@ -97,8 +107,7 @@ class ArchiveInPage:
|
|||||||
ft.Text(f"{client}", size=16, weight=ft.FontWeight.BOLD),
|
ft.Text(f"{client}", size=16, weight=ft.FontWeight.BOLD),
|
||||||
ft.Text(f"Order Number: {order['order_number']}", size=14),
|
ft.Text(f"Order Number: {order['order_number']}", size=14),
|
||||||
], expand=True),
|
], expand=True),
|
||||||
ft.Button("View",icon=ft.Icons.PICTURE_AS_PDF, on_click=lambda e, o=order: self.view_order(o)),
|
*buttons
|
||||||
ft.Button("Delete", icon=ft.Icons.CANCEL, on_click=lambda e, o=order: self.cancel_order(o))
|
|
||||||
]),
|
]),
|
||||||
padding=10,
|
padding=10,
|
||||||
border=ft.border.all(1, ft.Colors.GREY_300),
|
border=ft.border.all(1, ft.Colors.GREY_300),
|
||||||
@@ -108,6 +117,18 @@ class ArchiveInPage:
|
|||||||
)
|
)
|
||||||
self.page.update()
|
self.page.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()['orders_in'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
@@ -115,7 +136,7 @@ class ArchiveInPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Archive", size=24, weight=ft.FontWeight.BOLD),
|
ft.Text("Archive Orders In", size=24, weight=ft.FontWeight.BOLD),
|
||||||
ft.Button("Back", icon=ft.Icons.ARROW_BACK_IOS_NEW, on_click=self.on_go_back_btn_click)
|
ft.Button("Back", icon=ft.Icons.ARROW_BACK_IOS_NEW, on_click=self.on_go_back_btn_click)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
@@ -123,4 +144,30 @@ class ArchiveInPage:
|
|||||||
self.orders_list
|
self.orders_list
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Archive Orders 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
|
||||||
)
|
)
|
||||||
@@ -107,6 +107,18 @@ class ArchivePage:
|
|||||||
self.dashboard.placeholder.content = self.order_page.build()
|
self.dashboard.placeholder.content = self.order_page.build()
|
||||||
self.dashboard.placeholder.update()
|
self.dashboard.placeholder.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()['orders_out'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
@@ -114,7 +126,7 @@ class ArchivePage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Archive", size=24, weight=ft.FontWeight.BOLD),
|
ft.Text("Archive Orders Out", size=24, weight=ft.FontWeight.BOLD),
|
||||||
ft.Button("Back", icon=ft.Icons.ARROW_BACK_IOS_NEW, on_click=self.on_go_back_btn_click)
|
ft.Button("Back", icon=ft.Icons.ARROW_BACK_IOS_NEW, on_click=self.on_go_back_btn_click)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
@@ -122,4 +134,30 @@ class ArchivePage:
|
|||||||
self.orders_list
|
self.orders_list
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Archive Orders Out", 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
|
||||||
)
|
)
|
||||||
@@ -265,6 +265,18 @@ class ClientsPage:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error loading subscription:", e)
|
print("Error loading subscription:", e)
|
||||||
|
|
||||||
|
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()['clients'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.load_clients()
|
self.load_clients()
|
||||||
self.subscription = self.get_current_subscription_plan()
|
self.subscription = self.get_current_subscription_plan()
|
||||||
@@ -291,4 +303,30 @@ class ClientsPage:
|
|||||||
expand=True
|
expand=True
|
||||||
),
|
),
|
||||||
expand=True,
|
expand=True,
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Clients", 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
|
||||||
)
|
)
|
||||||
@@ -208,6 +208,18 @@ class DestinationsPage:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error loading subscription:", e)
|
print("Error loading subscription:", e)
|
||||||
|
|
||||||
|
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()['destinations'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.refresh()
|
self.refresh()
|
||||||
self.add_destination_btn = ft.ElevatedButton("Add Destination", icon=ft.Icons.ADD, on_click=lambda e: self.open_dialog())
|
self.add_destination_btn = ft.ElevatedButton("Add Destination", icon=ft.Icons.ADD, on_click=lambda e: self.open_dialog())
|
||||||
@@ -230,4 +242,30 @@ class DestinationsPage:
|
|||||||
self.destinations_column
|
self.destinations_column
|
||||||
],
|
],
|
||||||
expand=True,
|
expand=True,
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Adderess", 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
|
||||||
)
|
)
|
||||||
@@ -1196,7 +1196,7 @@ class OrdersEditPage:
|
|||||||
[
|
[
|
||||||
self.loading_date,
|
self.loading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.DatePicker(
|
ft.DatePicker(
|
||||||
@@ -1213,7 +1213,7 @@ class OrdersEditPage:
|
|||||||
[
|
[
|
||||||
self.loading_hour,
|
self.loading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1226,7 +1226,7 @@ class OrdersEditPage:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1289,7 +1289,7 @@ class OrdersEditPage:
|
|||||||
[
|
[
|
||||||
self.unloading_date,
|
self.unloading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.DatePicker(
|
ft.DatePicker(
|
||||||
@@ -1306,7 +1306,7 @@ class OrdersEditPage:
|
|||||||
[
|
[
|
||||||
self.unloading_hour,
|
self.unloading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1319,7 +1319,7 @@ class OrdersEditPage:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ class OrdersInPage:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.ldm_quantity = ft.TextField(
|
self.ldm_quantity = ft.TextField(
|
||||||
|
value='13.6',
|
||||||
expand=True,
|
expand=True,
|
||||||
keyboard_type=ft.KeyboardType.NUMBER,
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
@@ -159,7 +160,12 @@ class OrdersInPage:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.received_price = ft.TextField(
|
self.received_price = ft.TextField(
|
||||||
label="Price Received - visible only to you!",
|
label="Price Received",
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
|
)
|
||||||
|
self.expenses = ft.TextField(
|
||||||
|
label="Expenses",
|
||||||
keyboard_type=ft.KeyboardType.NUMBER,
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
||||||
)
|
)
|
||||||
@@ -193,6 +199,130 @@ class OrdersInPage:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.file_picker = ft.FilePicker(on_result=self.on_file_result)
|
||||||
|
self.page.overlay.append(self.file_picker)
|
||||||
|
|
||||||
|
# --- Persistent date & time pickers (must be on page.overlay) ---
|
||||||
|
self.loading_date_picker = ft.DatePicker(
|
||||||
|
first_date=datetime.datetime(year=2000, month=10, day=1),
|
||||||
|
last_date=datetime.datetime(year=2025, month=10, day=1),
|
||||||
|
on_change=self.on_loading_date_click,
|
||||||
|
)
|
||||||
|
self.unloading_date_picker = ft.DatePicker(
|
||||||
|
first_date=datetime.datetime(year=2000, month=10, day=1),
|
||||||
|
last_date=datetime.datetime(year=2025, month=10, day=1),
|
||||||
|
on_change=self.on_unloading_date_click,
|
||||||
|
)
|
||||||
|
self.loading_time_picker_start = ft.TimePicker(
|
||||||
|
confirm_text="Confirm",
|
||||||
|
error_invalid_text="Time out of range",
|
||||||
|
help_text="Pick your time slot",
|
||||||
|
on_change=self.on_loading_hour_click,
|
||||||
|
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
|
||||||
|
)
|
||||||
|
self.loading_time_picker_end = ft.TimePicker(
|
||||||
|
confirm_text="Confirm",
|
||||||
|
error_invalid_text="Time out of range",
|
||||||
|
help_text="Pick your time slot",
|
||||||
|
on_change=self.on_loading_hour_click,
|
||||||
|
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
|
||||||
|
)
|
||||||
|
self.unloading_time_picker_start = ft.TimePicker(
|
||||||
|
confirm_text="Confirm",
|
||||||
|
error_invalid_text="Time out of range",
|
||||||
|
help_text="Pick your time slot",
|
||||||
|
on_change=self.on_unloading_hour_click,
|
||||||
|
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
|
||||||
|
)
|
||||||
|
self.unloading_time_picker_end = ft.TimePicker(
|
||||||
|
confirm_text="Confirm",
|
||||||
|
error_invalid_text="Time out of range",
|
||||||
|
help_text="Pick your time slot",
|
||||||
|
on_change=self.on_unloading_hour_click,
|
||||||
|
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
|
||||||
|
)
|
||||||
|
# Attach to overlay
|
||||||
|
self.page.overlay.extend([
|
||||||
|
self.loading_date_picker,
|
||||||
|
self.unloading_date_picker,
|
||||||
|
self.loading_time_picker_start,
|
||||||
|
self.loading_time_picker_end,
|
||||||
|
self.unloading_time_picker_start,
|
||||||
|
self.unloading_time_picker_end,
|
||||||
|
])
|
||||||
|
#self.page.update()
|
||||||
|
|
||||||
|
self.upload_order_btn = ft.Button(
|
||||||
|
"Upload",
|
||||||
|
icon=ft.Icons.UPLOAD_FILE,
|
||||||
|
width=150,
|
||||||
|
on_click=self.on_upload_document_btn_click
|
||||||
|
)
|
||||||
|
|
||||||
|
self.filename = ft.Text()
|
||||||
|
|
||||||
|
def _open_date_picker(self, picker):
|
||||||
|
# Works on both newer and older Flet
|
||||||
|
if hasattr(picker, "pick_date"):
|
||||||
|
picker.pick_date()
|
||||||
|
else:
|
||||||
|
picker.open = True
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def _open_time_picker(self, picker):
|
||||||
|
# Works on both newer and older Flet
|
||||||
|
if hasattr(picker, "pick_time"):
|
||||||
|
picker.pick_time()
|
||||||
|
else:
|
||||||
|
picker.open = True
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_upload_document_btn_click(self, e):
|
||||||
|
# Ensure FilePicker is attached to the page (Portainer/Flet sometimes reuses page objects)
|
||||||
|
if self.file_picker not in getattr(self.page.overlay, "controls", []):
|
||||||
|
self.page.overlay.append(self.file_picker)
|
||||||
|
self.page.update()
|
||||||
|
self.file_picker.pick_files(
|
||||||
|
allow_multiple=False,
|
||||||
|
allowed_extensions=["png", "jpg", "jpeg", "pdf"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_file_result(self, e: ft.FilePickerResultEvent):
|
||||||
|
if not e.files:
|
||||||
|
return
|
||||||
|
file = e.files[0]
|
||||||
|
# Build a safe filename: order_in_YYYYmmdd_HHMMSS_userid_originalname
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
user_id = self.page.session.get("user_id")
|
||||||
|
new_filename = f"order_in_{timestamp}_{user_id}_{file.name}"
|
||||||
|
upload_url = self.page.get_upload_url(new_filename, 1000)
|
||||||
|
self.file_picker.upload([ft.FilePickerUploadFile(file.name, upload_url=upload_url)])
|
||||||
|
self.filename.value = new_filename
|
||||||
|
self.filename.update()
|
||||||
|
|
||||||
|
#import os
|
||||||
|
#import shutil
|
||||||
|
|
||||||
|
#source_path = os.path.join("uploads", new_filename)
|
||||||
|
# destination_path = os.path.join("assets/images", new_filename)
|
||||||
|
# try:
|
||||||
|
# time.sleep(2)
|
||||||
|
# shutil.move(source_path, destination_path)
|
||||||
|
# self.logo.src = f"images/{new_filename}"
|
||||||
|
# self.logo.update()
|
||||||
|
# self.logo_field.value = new_filename
|
||||||
|
# self.page.update()
|
||||||
|
# self.page.client_storage.set("logo_filename", new_filename)
|
||||||
|
# self.dashboard.logo.src = f"images/{new_filename}"
|
||||||
|
# self.dashboard.logo.update()
|
||||||
|
# except Exception as err:
|
||||||
|
# self.message.value = f"Upload error: {err}"
|
||||||
|
# self.message.color = ft.Colors.RED
|
||||||
|
# self.page.update()
|
||||||
|
|
||||||
|
|
||||||
def on_location_btn_click(self, destination):
|
def on_location_btn_click(self, destination):
|
||||||
query = destination["address"].replace(", ", "+")
|
query = destination["address"].replace(", ", "+")
|
||||||
maps_url = f"https://www.google.com/maps/search/?api=1&query={query}"
|
maps_url = f"https://www.google.com/maps/search/?api=1&query={query}"
|
||||||
@@ -266,7 +396,7 @@ class OrdersInPage:
|
|||||||
#print(self.loading_date.value)
|
#print(self.loading_date.value)
|
||||||
|
|
||||||
def on_loading_hour_click(self, e):
|
def on_loading_hour_click(self, e):
|
||||||
if len(self.loading_hour.value) != None and len(self.loading_hour.value)==0:
|
if not self.loading_hour.value:
|
||||||
self.loading_hour.value = str(e.control.value)
|
self.loading_hour.value = str(e.control.value)
|
||||||
else:
|
else:
|
||||||
self.loading_hour.value += f' - {e.control.value}'
|
self.loading_hour.value += f' - {e.control.value}'
|
||||||
@@ -322,10 +452,10 @@ class OrdersInPage:
|
|||||||
self.loading_error_message.value = "Please select loading point!"
|
self.loading_error_message.value = "Please select loading point!"
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
return
|
return
|
||||||
if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
|
# if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
|
||||||
self.loading_error_message.value = "Add loading informations!"
|
# self.loading_error_message.value = "Add loading informations!"
|
||||||
self.loading_error_message.update()
|
# self.loading_error_message.update()
|
||||||
return
|
# return
|
||||||
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
|
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
|
||||||
self.loading_error_message.value = "Add loading date!"
|
self.loading_error_message.value = "Add loading date!"
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
@@ -391,7 +521,7 @@ class OrdersInPage:
|
|||||||
self.unloading_date.update()
|
self.unloading_date.update()
|
||||||
|
|
||||||
def on_unloading_hour_click(self, e):
|
def on_unloading_hour_click(self, e):
|
||||||
if len(self.unloading_hour.value) != None and len(self.unloading_hour.value)==0:
|
if not self.unloading_hour.value:
|
||||||
self.unloading_hour.value = str(e.control.value)
|
self.unloading_hour.value = str(e.control.value)
|
||||||
else:
|
else:
|
||||||
self.unloading_hour.value += f' - {e.control.value}'
|
self.unloading_hour.value += f' - {e.control.value}'
|
||||||
@@ -440,10 +570,10 @@ class OrdersInPage:
|
|||||||
self.unloading_error_message.value = "Please select unloading point!"
|
self.unloading_error_message.value = "Please select unloading point!"
|
||||||
self.unloading_error_message.update()
|
self.unloading_error_message.update()
|
||||||
return
|
return
|
||||||
if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
|
# if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
|
||||||
self.unloading_error_message.value = "Add unloading informations!"
|
# self.unloading_error_message.value = "Add unloading informations!"
|
||||||
self.unloading_error_message.update()
|
# self.unloading_error_message.update()
|
||||||
return
|
# return
|
||||||
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
|
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
|
||||||
self.unloading_error_message.value = "Add unloading date!"
|
self.unloading_error_message.value = "Add unloading date!"
|
||||||
self.unloading_error_message.update()
|
self.unloading_error_message.update()
|
||||||
@@ -684,7 +814,9 @@ class OrdersInPage:
|
|||||||
'trailer_reg_number': self.trailer_reg_number.value,
|
'trailer_reg_number': self.trailer_reg_number.value,
|
||||||
'received_price': self.received_price.value,
|
'received_price': self.received_price.value,
|
||||||
'loading_addresses': loading_addresses,
|
'loading_addresses': loading_addresses,
|
||||||
'unloading_addresses': unloading_addresses
|
'unloading_addresses': unloading_addresses,
|
||||||
|
'file':self.filename.value,
|
||||||
|
'expenses': self.expenses.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:
|
||||||
@@ -763,6 +895,18 @@ class OrdersInPage:
|
|||||||
self.error_message.value = f"Error: {str(ex)}"
|
self.error_message.value = f"Error: {str(ex)}"
|
||||||
self.error_message.update()
|
self.error_message.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()['orders_in'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.save_btn = ft.FilledButton(
|
self.save_btn = ft.FilledButton(
|
||||||
"Save Order",
|
"Save Order",
|
||||||
@@ -785,7 +929,9 @@ class OrdersInPage:
|
|||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
|
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
|
||||||
self.order_number
|
self.order_number,
|
||||||
|
self.upload_order_btn,
|
||||||
|
self.filename
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -882,11 +1028,12 @@ class OrdersInPage:
|
|||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD)
|
ft.Text("Price / Expenses", size=18, weight=ft.FontWeight.BOLD)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.START
|
alignment=ft.MainAxisAlignment.START
|
||||||
),
|
),
|
||||||
self.received_price,
|
self.received_price,
|
||||||
|
self.expenses
|
||||||
],
|
],
|
||||||
expand=2.5
|
expand=2.5
|
||||||
)
|
)
|
||||||
@@ -926,15 +1073,9 @@ class OrdersInPage:
|
|||||||
[
|
[
|
||||||
self.loading_date,
|
self.loading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self._open_date_picker(self.loading_date_picker),
|
||||||
ft.DatePicker(
|
|
||||||
first_date=datetime.datetime(year=2000, month=10, day=1),
|
|
||||||
last_date=datetime.datetime(year=2025, month=10, day=1),
|
|
||||||
on_change=self.on_loading_date_click,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
expand=True
|
expand=True
|
||||||
@@ -943,30 +1084,14 @@ class OrdersInPage:
|
|||||||
[
|
[
|
||||||
self.loading_hour,
|
self.loading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self._open_time_picker(self.loading_time_picker_start),
|
||||||
ft.TimePicker(
|
|
||||||
confirm_text="Confirm",
|
|
||||||
error_invalid_text="Time out of range",
|
|
||||||
help_text="Pick your time slot",
|
|
||||||
on_change=self.on_loading_hour_click,
|
|
||||||
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self._open_time_picker(self.loading_time_picker_end),
|
||||||
ft.TimePicker(
|
|
||||||
confirm_text="Confirm",
|
|
||||||
error_invalid_text="Time out of range",
|
|
||||||
help_text="Pick your time slot",
|
|
||||||
on_change=self.on_loading_hour_click,
|
|
||||||
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Reset",
|
"Reset",
|
||||||
@@ -1019,15 +1144,9 @@ class OrdersInPage:
|
|||||||
[
|
[
|
||||||
self.unloading_date,
|
self.unloading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self._open_date_picker(self.unloading_date_picker),
|
||||||
ft.DatePicker(
|
|
||||||
first_date=datetime.datetime(year=2000, month=10, day=1),
|
|
||||||
last_date=datetime.datetime(year=2025, month=10, day=1),
|
|
||||||
on_change=self.on_unloading_date_click,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
expand=True
|
expand=True
|
||||||
@@ -1036,30 +1155,14 @@ class OrdersInPage:
|
|||||||
[
|
[
|
||||||
self.unloading_hour,
|
self.unloading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self._open_time_picker(self.unloading_time_picker_start),
|
||||||
ft.TimePicker(
|
|
||||||
confirm_text="Confirm",
|
|
||||||
error_invalid_text="Time out of range",
|
|
||||||
help_text="Pick your time slot",
|
|
||||||
on_change=self.on_unloading_hour_click,
|
|
||||||
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self._open_time_picker(self.unloading_time_picker_end),
|
||||||
ft.TimePicker(
|
|
||||||
confirm_text="Confirm",
|
|
||||||
error_invalid_text="Time out of range",
|
|
||||||
help_text="Pick your time slot",
|
|
||||||
on_change=self.on_unloading_hour_click,
|
|
||||||
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Reset",
|
"Reset",
|
||||||
@@ -1101,5 +1204,31 @@ class OrdersInPage:
|
|||||||
scroll=ft.ScrollMode.ADAPTIVE,
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
spacing=20
|
spacing=20
|
||||||
)
|
)
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Create 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
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -156,7 +156,8 @@ class OrdersOutPage:
|
|||||||
self.ldm_quantity = ft.TextField(
|
self.ldm_quantity = ft.TextField(
|
||||||
expand=True,
|
expand=True,
|
||||||
keyboard_type=ft.KeyboardType.NUMBER,
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
|
||||||
|
value=13.6
|
||||||
)
|
)
|
||||||
|
|
||||||
self.kg_quantity = ft.TextField(
|
self.kg_quantity = ft.TextField(
|
||||||
@@ -402,10 +403,10 @@ class OrdersOutPage:
|
|||||||
self.loading_error_message.value = "Please select loading point!"
|
self.loading_error_message.value = "Please select loading point!"
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
return
|
return
|
||||||
if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
|
# if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
|
||||||
self.loading_error_message.value = "Add loading informations!"
|
# self.loading_error_message.value = "Add loading informations!"
|
||||||
self.loading_error_message.update()
|
# self.loading_error_message.update()
|
||||||
return
|
# return
|
||||||
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
|
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
|
||||||
self.loading_error_message.value = "Add loading date!"
|
self.loading_error_message.value = "Add loading date!"
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
@@ -520,10 +521,10 @@ class OrdersOutPage:
|
|||||||
self.unloading_error_message.value = "Please select unloading point!"
|
self.unloading_error_message.value = "Please select unloading point!"
|
||||||
self.unloading_error_message.update()
|
self.unloading_error_message.update()
|
||||||
return
|
return
|
||||||
if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
|
# if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
|
||||||
self.unloading_error_message.value = "Add unloading informations!"
|
# self.unloading_error_message.value = "Add unloading informations!"
|
||||||
self.unloading_error_message.update()
|
# self.unloading_error_message.update()
|
||||||
return
|
# return
|
||||||
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
|
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
|
||||||
self.unloading_error_message.value = "Add unloading date!"
|
self.unloading_error_message.value = "Add unloading date!"
|
||||||
self.unloading_error_message.update()
|
self.unloading_error_message.update()
|
||||||
@@ -868,6 +869,18 @@ class OrdersOutPage:
|
|||||||
print("Error loading subscription:", e)
|
print("Error loading subscription:", e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
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()['orders_out'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.save_btn = ft.FilledButton(
|
self.save_btn = ft.FilledButton(
|
||||||
"Save and Generate",
|
"Save and Generate",
|
||||||
@@ -1050,7 +1063,7 @@ class OrdersOutPage:
|
|||||||
[
|
[
|
||||||
self.loading_date,
|
self.loading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.DatePicker(
|
ft.DatePicker(
|
||||||
@@ -1067,7 +1080,7 @@ class OrdersOutPage:
|
|||||||
[
|
[
|
||||||
self.loading_hour,
|
self.loading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1080,7 +1093,7 @@ class OrdersOutPage:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1143,7 +1156,7 @@ class OrdersOutPage:
|
|||||||
[
|
[
|
||||||
self.unloading_date,
|
self.unloading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.DatePicker(
|
ft.DatePicker(
|
||||||
@@ -1160,7 +1173,7 @@ class OrdersOutPage:
|
|||||||
[
|
[
|
||||||
self.unloading_hour,
|
self.unloading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1173,7 +1186,7 @@ class OrdersOutPage:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1225,5 +1238,31 @@ class OrdersOutPage:
|
|||||||
scroll=ft.ScrollMode.ADAPTIVE,
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
spacing=20
|
spacing=20
|
||||||
)
|
)
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Create Order Out", 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,6 +1,8 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
from pages.orders_in_page import OrdersInPage
|
from pages.orders_in_page import OrdersInPage
|
||||||
from pages.orders_out_page import OrdersOutPage
|
from pages.orders_out_page import OrdersOutPage
|
||||||
|
from pages.archive_in_page import ArchiveInPage
|
||||||
|
from pages.archive_page import ArchivePage
|
||||||
|
|
||||||
class OrdersPage:
|
class OrdersPage:
|
||||||
def __init__(self, page: ft.Page, dashboard):
|
def __init__(self, page: ft.Page, dashboard):
|
||||||
@@ -17,6 +19,16 @@ class OrdersPage:
|
|||||||
self.dashboard.placeholder.content = orders_out_page.build()
|
self.dashboard.placeholder.content = orders_out_page.build()
|
||||||
self.dashboard.placeholder.update()
|
self.dashboard.placeholder.update()
|
||||||
|
|
||||||
|
def on_archive_order_out_btn_click(self, e):
|
||||||
|
archive = ArchivePage(self.page, self.dashboard, self)
|
||||||
|
self.dashboard.placeholder.content = archive.build()
|
||||||
|
self.dashboard.placeholder.update()
|
||||||
|
|
||||||
|
def on_archive_order_in_btn_click(self, e):
|
||||||
|
archive = ArchiveInPage(self.page, self.dashboard, self)
|
||||||
|
self.dashboard.placeholder.content = archive.build()
|
||||||
|
self.dashboard.placeholder.update()
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
content=ft.Column(
|
content=ft.Column(
|
||||||
@@ -31,7 +43,7 @@ class OrdersPage:
|
|||||||
ft.Container(
|
ft.Container(
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Incoming orders", size=20)
|
ft.Text("Orders In", size=20)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.CENTER
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
),
|
),
|
||||||
@@ -39,12 +51,23 @@ class OrdersPage:
|
|||||||
width=250,
|
width=250,
|
||||||
height=80
|
height=80
|
||||||
),
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
ft.FilledButton(
|
ft.FilledButton(
|
||||||
"Orders In",
|
"Create",
|
||||||
on_click=self.on_orders_in_btn_click,
|
on_click=self.on_orders_in_btn_click,
|
||||||
width=150
|
width=100
|
||||||
|
),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Archive",
|
||||||
|
on_click=self.on_archive_order_in_btn_click,
|
||||||
|
width=100
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_EVENLY
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
),
|
),
|
||||||
@@ -62,7 +85,7 @@ class OrdersPage:
|
|||||||
ft.Container(
|
ft.Container(
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Outcoming orders", size=20)
|
ft.Text("Orders Out", size=20)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.CENTER
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
),
|
),
|
||||||
@@ -70,10 +93,20 @@ class OrdersPage:
|
|||||||
width=250,
|
width=250,
|
||||||
height=80
|
height=80
|
||||||
),
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
ft.FilledButton(
|
ft.FilledButton(
|
||||||
"Orders Out",
|
"Create",
|
||||||
on_click=self.on_orders_out_btn_click,
|
on_click=self.on_orders_out_btn_click,
|
||||||
width=150
|
width=100
|
||||||
|
),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Archive",
|
||||||
|
on_click=self.on_archive_order_out_btn_click,
|
||||||
|
width=100
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_EVENLY
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.CENTER,
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import flet as ft
|
|||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
from config import API_BASE_URL
|
from config import API_BASE_URL
|
||||||
|
from pages.users_page import Users
|
||||||
|
|
||||||
class ProfilePage:
|
class ProfilePage:
|
||||||
def __init__(self, page: ft.Page, dashboard):
|
def __init__(self, page: ft.Page, dashboard):
|
||||||
@@ -138,9 +139,9 @@ class ProfilePage:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.created_at_text = ft.Text(value="Created At: TBD") # Set dynamically later
|
self.created_at_text = ft.Text(value="Created At: TBD") # Set dynamically later
|
||||||
self.edit_button = ft.ElevatedButton(text="Edit Profile", on_click=self.on_edit_click)
|
self.edit_button = ft.ElevatedButton(text="Edit Profile", on_click=self.on_edit_click, width=100)
|
||||||
self.save_button = ft.ElevatedButton(text="Save Changes", visible=False, on_click=self.on_save_click)
|
self.save_button = ft.ElevatedButton(text="Save Changes", visible=False, on_click=self.on_save_click, width=130)
|
||||||
self.cancel_button = ft.TextButton(text="Cancel", visible=False, on_click=self.on_cancel_click)
|
self.cancel_button = ft.ElevatedButton(text="Cancel", visible=False, on_click=self.on_cancel_click, width=100)
|
||||||
self.upload_logo_btn = ft.ElevatedButton("Upload Company Logo", icon=ft.Icons.UPLOAD, on_click=self.on_upload_click, disabled=True)
|
self.upload_logo_btn = ft.ElevatedButton("Upload Company Logo", icon=ft.Icons.UPLOAD, on_click=self.on_upload_click, disabled=True)
|
||||||
self.message = ft.Text()
|
self.message = ft.Text()
|
||||||
self.logo = ft.Image(src="images/image_placeholder.png", width=250)
|
self.logo = ft.Image(src="images/image_placeholder.png", width=250)
|
||||||
@@ -177,6 +178,18 @@ class ProfilePage:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.add_users = ft.Button(
|
||||||
|
"Add Users",
|
||||||
|
icon=ft.Icons.MANAGE_ACCOUNTS_ROUNDED,
|
||||||
|
width=250,
|
||||||
|
on_click=self.on_add_users_btn_click
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_add_users_btn_click(self, e):
|
||||||
|
users = Users(self.page, self.dashboard)
|
||||||
|
self.dashboard.placeholder.content = users.build()
|
||||||
|
self.dashboard.placeholder.update()
|
||||||
|
|
||||||
def _auth_headers(self):
|
def _auth_headers(self):
|
||||||
"""Build Authorization header from client storage robustly (web/desktop)."""
|
"""Build Authorization header from client storage robustly (web/desktop)."""
|
||||||
t = self.page.client_storage.get("token")
|
t = self.page.client_storage.get("token")
|
||||||
@@ -408,12 +421,22 @@ class ProfilePage:
|
|||||||
self.message.color = ft.Colors.RED
|
self.message.color = ft.Colors.RED
|
||||||
self.page.update()
|
self.page.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:
|
||||||
|
return False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.populate_user_data()
|
self.populate_user_data()
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
content=ft.Column(
|
content=ft.Column(
|
||||||
[
|
[
|
||||||
ft.Text("User Profile", size=24, weight=ft.FontWeight.BOLD),
|
ft.Text("Company Profile", size=24, weight=ft.FontWeight.BOLD),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Column(
|
ft.Column(
|
||||||
@@ -425,7 +448,10 @@ class ProfilePage:
|
|||||||
self.smtp_user,
|
self.smtp_user,
|
||||||
self.smtp_host,
|
self.smtp_host,
|
||||||
self.smtp_port,
|
self.smtp_port,
|
||||||
ft.Row([self.edit_button, self.save_button, self.cancel_button])
|
ft.Text(),
|
||||||
|
ft.Row([self.edit_button, self.save_button, self.cancel_button]),
|
||||||
|
ft.Divider(),
|
||||||
|
self.add_users,ft.Text()
|
||||||
],
|
],
|
||||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
width=250
|
width=250
|
||||||
@@ -455,4 +481,30 @@ class ProfilePage:
|
|||||||
],
|
],
|
||||||
scroll=ft.ScrollMode.ADAPTIVE
|
scroll=ft.ScrollMode.ADAPTIVE
|
||||||
),
|
),
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Company Profile", 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
|
||||||
)
|
)
|
||||||
@@ -326,6 +326,18 @@ class ReportPage:
|
|||||||
self.total.value = f"Total: {total}"
|
self.total.value = f"Total: {total}"
|
||||||
self.total.update()
|
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):
|
def build(self):
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
content=ft.Column(
|
content=ft.Column(
|
||||||
@@ -386,4 +398,30 @@ class ReportPage:
|
|||||||
alignment=ft.MainAxisAlignment.START,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
),
|
),
|
||||||
expand=True
|
expand=True
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Reports", 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
|
||||||
)
|
)
|
||||||
@@ -93,6 +93,16 @@ class Subscription:
|
|||||||
self.current_subscription_status.value = self.status[status]
|
self.current_subscription_status.value = self.status[status]
|
||||||
self.current_subscription_status.update()
|
self.current_subscription_status.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:
|
||||||
|
return False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
content=ft.Column(
|
content=ft.Column(
|
||||||
@@ -244,4 +254,30 @@ class Subscription:
|
|||||||
spacing=50
|
spacing=50
|
||||||
),
|
),
|
||||||
expand=True
|
expand=True
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Subscription", 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
|
||||||
)
|
)
|
||||||
60
transportmanager/client/pages/temporary_password_page.py
Normal file
60
transportmanager/client/pages/temporary_password_page.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import flet as ft
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
from config import API_BASE_URL
|
||||||
|
import re
|
||||||
|
from pages.dashboard_page import DashboardPage
|
||||||
|
|
||||||
|
class TemporaryPassword:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.password = ft.TextField(label="Password", password=True, can_reveal_password=True, width=300)
|
||||||
|
self.confirm_password = ft.TextField(label="Confirm Password", password=True, can_reveal_password=True, width=300)
|
||||||
|
self.message = ft.Text("", color=ft.Colors.RED, text_align=ft.TextAlign.CENTER)
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
# Password strength validation
|
||||||
|
if len(self.password.value) < 8 or not re.search(r"[A-Z]", self.password.value) or not re.search(r"[0-9]", self.password.value):
|
||||||
|
self.message.value = "Password must be at least 8 characters long and include a number and a capital letter"
|
||||||
|
self.message.update()
|
||||||
|
return
|
||||||
|
# Placeholder for register logic
|
||||||
|
if self.password.value != self.confirm_password.value:
|
||||||
|
self.message.value = "Passwords do not match"
|
||||||
|
self.message.update()
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.post(f"{API_BASE_URL}/auth/temporary_password", headers=headers, json={
|
||||||
|
"password": self.password.value
|
||||||
|
})
|
||||||
|
if response.status_code == 200:
|
||||||
|
self.message.color = ft.Colors.GREEN
|
||||||
|
self.message.value = "Password updated!\nYou will be redirected to Dashboard"
|
||||||
|
self.message.update()
|
||||||
|
time.sleep(3)
|
||||||
|
# Proceed to Dashboard
|
||||||
|
self.page.go("/dashboard")
|
||||||
|
else:
|
||||||
|
self.message.color = ft.Colors.RED
|
||||||
|
self.message.value = response.json().get("message", "Password update fail!")
|
||||||
|
self.message.update()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("This is your first login,\nplease change your password!", weight=ft.FontWeight.BOLD, size=20, text_align=ft.TextAlign.CENTER),
|
||||||
|
self.password,
|
||||||
|
self.confirm_password,
|
||||||
|
self.message,
|
||||||
|
ft.Button("Save", width=150, on_click=self.on_save_btn_click)
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
spacing=20,
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -245,6 +245,18 @@ class TransportersPage:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error loading subscription:", e)
|
print("Error loading subscription:", e)
|
||||||
|
|
||||||
|
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()['transporters'] == 1 else False
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
self.transporter_list = ft.Column(spacing=10, expand=True, scroll=ft.ScrollMode.ADAPTIVE,)
|
self.transporter_list = ft.Column(spacing=10, expand=True, scroll=ft.ScrollMode.ADAPTIVE,)
|
||||||
self.refresh()
|
self.refresh()
|
||||||
@@ -270,4 +282,30 @@ class TransportersPage:
|
|||||||
],
|
],
|
||||||
alignment=ft.MainAxisAlignment.START,
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
|
||||||
|
) if self.get_client_access() else ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Transporters", 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,6 +1,7 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
import requests
|
import requests
|
||||||
from config import API_BASE_URL
|
from config import API_BASE_URL
|
||||||
|
from pages.temporary_password_page import TemporaryPassword
|
||||||
|
|
||||||
class TwoFactorAuth:
|
class TwoFactorAuth:
|
||||||
def __init__(self, page: ft.Page, email: str, login, auth):
|
def __init__(self, page: ft.Page, email: str, login, auth):
|
||||||
@@ -34,11 +35,31 @@ class TwoFactorAuth:
|
|||||||
if user_info.get("user_role") == 'admin':
|
if user_info.get("user_role") == 'admin':
|
||||||
print('Admin Logged In')
|
print('Admin Logged In')
|
||||||
self.page.go("/admin")
|
self.page.go("/admin")
|
||||||
|
else:
|
||||||
|
ui = requests.get(
|
||||||
|
f"{API_BASE_URL}/auth/me",
|
||||||
|
headers={"Authorization": f"Bearer {token}"},
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
if ui.status_code == 200:
|
||||||
|
uj = ui.json()
|
||||||
|
try:
|
||||||
|
logo_filename = uj.get("logo_filename", "")
|
||||||
|
if logo_filename:
|
||||||
|
self.page.client_storage.set("custom_logo", logo_filename)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if uj['temporary_password'] == 1:
|
||||||
|
self.auth.placeholder.content.clean()
|
||||||
|
temp_passwd_page = TemporaryPassword(self.page)
|
||||||
|
self.auth.placeholder.content = temp_passwd_page.build()
|
||||||
|
self.auth.placeholder.update()
|
||||||
else:
|
else:
|
||||||
self.success_text.value = "Verification successful. You are now logged in."
|
self.success_text.value = "Verification successful. You are now logged in."
|
||||||
self.error_text.value = ""
|
self.error_text.value = ""
|
||||||
self.page.update()
|
self.page.update()
|
||||||
self.page.go("/dashboard") # Change this to your main page
|
self.page.go("/dashboard")
|
||||||
else:
|
else:
|
||||||
self.error_text.value = "Invalid or expired code."
|
self.error_text.value = "Invalid or expired code."
|
||||||
self.success_text.value = ""
|
self.success_text.value = ""
|
||||||
|
|||||||
274
transportmanager/client/pages/users_page.py
Normal file
274
transportmanager/client/pages/users_page.py
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
import flet as ft
|
||||||
|
import requests
|
||||||
|
from config import API_BASE_URL
|
||||||
|
|
||||||
|
class Users:
|
||||||
|
def __init__(self, page: ft.Page, dashboard):
|
||||||
|
self.page = page
|
||||||
|
self.dashboard = dashboard
|
||||||
|
self.users = self.get_users()
|
||||||
|
self.selected_user = None
|
||||||
|
self.users_list = ft.ListView(
|
||||||
|
controls=self.create_users_list(self.users, self.on_edit_btn_click, self.on_delete_btn_click),
|
||||||
|
spacing=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
self.add_user_dialog = ft.AlertDialog(
|
||||||
|
title="Add Company User",
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.TextField(label="Name"),
|
||||||
|
ft.TextField(label="Email"),
|
||||||
|
ft.TextField(label="Temporary Password", password=True, can_reveal_password=True),
|
||||||
|
ft.Text("Give user access to add and modify:"),
|
||||||
|
ft.Checkbox(label="Orders In"),
|
||||||
|
ft.Checkbox(label="Order Out"),
|
||||||
|
ft.Checkbox(label="Addresses"),
|
||||||
|
ft.Checkbox(label="Clients"),
|
||||||
|
ft.Checkbox(label="Transporters"),
|
||||||
|
ft.Checkbox(label="Report"),
|
||||||
|
ft.Text("", color=ft.Colors.RED)
|
||||||
|
],
|
||||||
|
width=600,
|
||||||
|
height=500
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.TextButton(
|
||||||
|
"Cancel",
|
||||||
|
width=100,
|
||||||
|
on_click=self.on_cancel_btn_click
|
||||||
|
),
|
||||||
|
ft.ElevatedButton(
|
||||||
|
"Save",
|
||||||
|
width=100,
|
||||||
|
on_click=self.on_save_btn_click
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.delete_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Delete User?"),
|
||||||
|
actions=[
|
||||||
|
ft.TextButton("Cancel", on_click=self.on_cancel_delete_btn_click, width=100),
|
||||||
|
ft.Button("Yes", on_click=self.on_confirm_delete_btn_click, width=100),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.edit_user_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text(f"Edit User"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Checkbox(label="Orders In"),
|
||||||
|
ft.Checkbox(label="Order Out"),
|
||||||
|
ft.Checkbox(label="Addresses"),
|
||||||
|
ft.Checkbox(label="Clients"),
|
||||||
|
ft.Checkbox(label="Transporters"),
|
||||||
|
ft.Checkbox(label="Report"),
|
||||||
|
],
|
||||||
|
width=400,
|
||||||
|
height=350
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.TextButton(
|
||||||
|
"Cancel",
|
||||||
|
width=100,
|
||||||
|
on_click=self.on_edit_cancel_btn_click
|
||||||
|
),
|
||||||
|
ft.ElevatedButton(
|
||||||
|
"Save",
|
||||||
|
width=100,
|
||||||
|
on_click=self.on_edit_save_btn_click
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_edit_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.edit_user_dialog)
|
||||||
|
|
||||||
|
def on_edit_save_btn_click(self, e):
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
access_payload = {
|
||||||
|
'company_user_id':self.selected_user['id'],
|
||||||
|
'orders_in':1 if self.edit_user_dialog.content.controls[0].value else 0,
|
||||||
|
'orders_out':1 if self.edit_user_dialog.content.controls[1].value else 0,
|
||||||
|
'addresses':1 if self.edit_user_dialog.content.controls[2].value else 0,
|
||||||
|
'clients':1 if self.edit_user_dialog.content.controls[3].value else 0,
|
||||||
|
'transporters':1 if self.edit_user_dialog.content.controls[4].value else 0,
|
||||||
|
'report':1 if self.edit_user_dialog.content.controls[5].value else 0
|
||||||
|
}
|
||||||
|
response = requests.put(f"{API_BASE_URL}/company_user/update_access", headers=headers, json=access_payload)
|
||||||
|
|
||||||
|
#set to default
|
||||||
|
self.selected_user = None
|
||||||
|
self.page.close(self.edit_user_dialog)
|
||||||
|
self.edit_user_dialog.content.controls[0].value = False
|
||||||
|
self.edit_user_dialog.content.controls[1].value = False
|
||||||
|
self.edit_user_dialog.content.controls[2].value = False
|
||||||
|
self.edit_user_dialog.content.controls[3].value = False
|
||||||
|
self.edit_user_dialog.content.controls[4].value = False
|
||||||
|
self.edit_user_dialog.content.controls[5].value = False
|
||||||
|
self.edit_user_dialog.update()
|
||||||
|
|
||||||
|
def on_confirm_delete_btn_click(self, e):
|
||||||
|
self.page.close(self.delete_dialog)
|
||||||
|
try:
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
delete_payload = {
|
||||||
|
'user_id':self.selected_user['id']
|
||||||
|
}
|
||||||
|
response = requests.post(f"{API_BASE_URL}/admin/users/deactivate", headers=headers, json=delete_payload)
|
||||||
|
except Exception as e:
|
||||||
|
print("Error deleting company users:", e)
|
||||||
|
users = self.get_users()
|
||||||
|
self.users_list.controls.clear()
|
||||||
|
self.users_list.controls = self.create_users_list(users, self.on_edit_btn_click, self.on_delete_btn_click)
|
||||||
|
self.users_list.update()
|
||||||
|
self.selected_user = None
|
||||||
|
|
||||||
|
def on_cancel_delete_btn_click(self, e):
|
||||||
|
self.page.close(self.delete_dialog)
|
||||||
|
|
||||||
|
def add_new_user(self, e):
|
||||||
|
self.page.open(self.add_user_dialog)
|
||||||
|
|
||||||
|
def on_edit_btn_click(self, item):
|
||||||
|
self.selected_user = item
|
||||||
|
access = self.get_user_access(item['id'])
|
||||||
|
self.edit_user_dialog.content.controls[0].value = True if access['orders_in']==1 else False
|
||||||
|
self.edit_user_dialog.content.controls[1].value = True if access['orders_out']==1 else False
|
||||||
|
self.edit_user_dialog.content.controls[2].value = True if access['destinations']==1 else False
|
||||||
|
self.edit_user_dialog.content.controls[3].value = True if access['clients']==1 else False
|
||||||
|
self.edit_user_dialog.content.controls[4].value = True if access['transporters']==1 else False
|
||||||
|
self.edit_user_dialog.content.controls[5].value = True if access['report']==1 else False
|
||||||
|
self.page.open(self.edit_user_dialog)
|
||||||
|
|
||||||
|
def get_user_access(self, id):
|
||||||
|
try:
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
|
||||||
|
return response.json()
|
||||||
|
except Exception as e:
|
||||||
|
print("Error loading company user access:", e)
|
||||||
|
|
||||||
|
def on_delete_btn_click(self, item):
|
||||||
|
self.selected_user = item
|
||||||
|
self.page.open(self.delete_dialog)
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
self.page.close(self.add_user_dialog)
|
||||||
|
try:
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
create_payload = {
|
||||||
|
'name':self.add_user_dialog.content.controls[0].value,
|
||||||
|
'email':self.add_user_dialog.content.controls[1].value,
|
||||||
|
'company_id':self.page.session.get("user_id"),
|
||||||
|
'password':self.add_user_dialog.content.controls[2].value
|
||||||
|
}
|
||||||
|
response = requests.post(f"{API_BASE_URL}/company_user/register_company_user", headers=headers, json=create_payload)
|
||||||
|
company_user_id = response.json()['company_user_id'] if response.status_code == 201 else self.show_error_mesage
|
||||||
|
if company_user_id:
|
||||||
|
access_payload = {
|
||||||
|
'company_user_id':company_user_id,
|
||||||
|
'orders_in':1 if self.add_user_dialog.content.controls[4].value else 0,
|
||||||
|
'orders_out':1 if self.add_user_dialog.content.controls[5].value else 0,
|
||||||
|
'addresses':1 if self.add_user_dialog.content.controls[6].value else 0,
|
||||||
|
'clients':1 if self.add_user_dialog.content.controls[7].value else 0,
|
||||||
|
'transporters':1 if self.add_user_dialog.content.controls[8].value else 0,
|
||||||
|
'report':1 if self.add_user_dialog.content.controls[9].value else 0
|
||||||
|
}
|
||||||
|
response = requests.post(f"{API_BASE_URL}/company_user/access", headers=headers, json=access_payload)
|
||||||
|
except Exception as e:
|
||||||
|
print("Error loading company users:", e)
|
||||||
|
|
||||||
|
users = self.get_users()
|
||||||
|
self.users_list.controls.clear()
|
||||||
|
self.users_list.controls = self.create_users_list(users, self.on_edit_btn_click, self.on_delete_btn_click)
|
||||||
|
self.users_list.update()
|
||||||
|
#set dialog to default valuse:
|
||||||
|
self.add_user_dialog.content.controls[0].value = ''
|
||||||
|
self.add_user_dialog.content.controls[1].value = ''
|
||||||
|
self.add_user_dialog.content.controls[2].value = ''
|
||||||
|
self.add_user_dialog.content.controls[4].value = False
|
||||||
|
self.add_user_dialog.content.controls[5].value = False
|
||||||
|
self.add_user_dialog.content.controls[6].value = False
|
||||||
|
self.add_user_dialog.content.controls[7].value = False
|
||||||
|
self.add_user_dialog.content.controls[8].value = False
|
||||||
|
self.add_user_dialog.content.controls[9].value = False
|
||||||
|
self.add_user_dialog.content.controls[10].value = ""
|
||||||
|
self.add_user_dialog.update()
|
||||||
|
|
||||||
|
def show_error_mesage(self):
|
||||||
|
self.add_user_dialog.content.controls[10].value = "A user with this email has been already created, to reactivate it send a request at office@ordergo.eu"
|
||||||
|
self.add_user_dialog.update()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.add_user_dialog)
|
||||||
|
|
||||||
|
def get_users(self):
|
||||||
|
try:
|
||||||
|
user_id = self.page.session.get("user_id")
|
||||||
|
users = []
|
||||||
|
token = self.page.client_storage.get("token")
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
response = requests.get(f"{API_BASE_URL}/admin/users/company_users", headers=headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
all_users=response.json()
|
||||||
|
for user in all_users:
|
||||||
|
if user['company_id'] == user_id and user['active'] == 1:
|
||||||
|
users.append(user)
|
||||||
|
return users
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
print("Error loading company users:", e)
|
||||||
|
|
||||||
|
def create_users_list(self, items, on_click_handler, on_click_handler2):
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(item['name'], expand=True, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(item['email'], size=12)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Button("Edit", on_click=lambda e, id=item: on_click_handler(id), width=100),
|
||||||
|
ft.FilledButton("Delete", on_click=lambda e, id=item: on_click_handler2(id), width=100, bgcolor=ft.Colors.RED)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BLUE_50,
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
border=ft.border.all(1, ft.Colors.GREY_300),
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content= ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Users", size=24, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Button("Add User", icon=ft.Icons.ADD, on_click=self.add_new_user)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
self.users_list
|
||||||
|
],
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
@@ -188,6 +188,12 @@ class ViewOrdersIn:
|
|||||||
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
|
||||||
value=self.order['received_price']
|
value=self.order['received_price']
|
||||||
)
|
)
|
||||||
|
self.expenses = ft.TextField(
|
||||||
|
label="Expenses",
|
||||||
|
keyboard_type=ft.KeyboardType.NUMBER,
|
||||||
|
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
|
||||||
|
value=self.order['expenses']
|
||||||
|
)
|
||||||
|
|
||||||
self.error_message = ft.Text(color = ft.Colors.RED)
|
self.error_message = ft.Text(color = ft.Colors.RED)
|
||||||
|
|
||||||
@@ -280,6 +286,18 @@ class ViewOrdersIn:
|
|||||||
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.file_picker = ft.FilePicker(on_result=self.on_file_result)
|
||||||
|
self.page.overlay.append(self.file_picker)
|
||||||
|
|
||||||
|
self.upload_order_btn = ft.Button(
|
||||||
|
"Upload",
|
||||||
|
icon=ft.Icons.UPLOAD_FILE,
|
||||||
|
width=150,
|
||||||
|
on_click=self.on_upload_document_btn_click
|
||||||
|
)
|
||||||
|
|
||||||
|
self.filename = ft.Text(value=self.order['file'])
|
||||||
|
|
||||||
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()
|
||||||
@@ -447,10 +465,10 @@ class ViewOrdersIn:
|
|||||||
self.loading_error_message.value = "Please select loading point!"
|
self.loading_error_message.value = "Please select loading point!"
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
return
|
return
|
||||||
if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
|
# if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
|
||||||
self.loading_error_message.value = "Add loading informations!"
|
# self.loading_error_message.value = "Add loading informations!"
|
||||||
self.loading_error_message.update()
|
# self.loading_error_message.update()
|
||||||
return
|
# return
|
||||||
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
|
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
|
||||||
self.loading_error_message.value = "Add loading date!"
|
self.loading_error_message.value = "Add loading date!"
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
@@ -480,6 +498,29 @@ class ViewOrdersIn:
|
|||||||
self.loading_error_message.value = "All fields of the loading address are required."
|
self.loading_error_message.value = "All fields of the loading address are required."
|
||||||
self.loading_error_message.update()
|
self.loading_error_message.update()
|
||||||
|
|
||||||
|
def on_upload_document_btn_click(self, e):
|
||||||
|
# Ensure FilePicker is attached to the page (Portainer/Flet sometimes reuses page objects)
|
||||||
|
if self.file_picker not in getattr(self.page.overlay, "controls", []):
|
||||||
|
self.page.overlay.append(self.file_picker)
|
||||||
|
self.page.update()
|
||||||
|
self.file_picker.pick_files(
|
||||||
|
allow_multiple=False,
|
||||||
|
allowed_extensions=["png", "jpg", "jpeg", "pdf"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_file_result(self, e: ft.FilePickerResultEvent):
|
||||||
|
if not e.files:
|
||||||
|
return
|
||||||
|
file = e.files[0]
|
||||||
|
# Build a safe filename: order_in_YYYYmmdd_HHMMSS_userid_originalname
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
user_id = self.page.session.get("user_id")
|
||||||
|
new_filename = f"order_in_{timestamp}_{user_id}_{file.name}"
|
||||||
|
upload_url = self.page.get_upload_url(new_filename, 1000)
|
||||||
|
self.file_picker.upload([ft.FilePickerUploadFile(file.name, upload_url=upload_url)])
|
||||||
|
self.filename.value = new_filename
|
||||||
|
self.filename.update()
|
||||||
|
|
||||||
def on_searching_unloading_address(self, e):
|
def on_searching_unloading_address(self, e):
|
||||||
query = e.control.value.lower()
|
query = e.control.value.lower()
|
||||||
self.filtered_addresses_ul = [a for a in self.all_addresses if query in a["name"].lower()]
|
self.filtered_addresses_ul = [a for a in self.all_addresses if query in a["name"].lower()]
|
||||||
@@ -565,10 +606,10 @@ class ViewOrdersIn:
|
|||||||
self.unloading_error_message.value = "Please select unloading point!"
|
self.unloading_error_message.value = "Please select unloading point!"
|
||||||
self.unloading_error_message.update()
|
self.unloading_error_message.update()
|
||||||
return
|
return
|
||||||
if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
|
# if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
|
||||||
self.unloading_error_message.value = "Add unloading informations!"
|
# self.unloading_error_message.value = "Add unloading informations!"
|
||||||
self.unloading_error_message.update()
|
# self.unloading_error_message.update()
|
||||||
return
|
# return
|
||||||
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
|
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
|
||||||
self.unloading_error_message.value = "Add unloading date!"
|
self.unloading_error_message.value = "Add unloading date!"
|
||||||
self.unloading_error_message.update()
|
self.unloading_error_message.update()
|
||||||
@@ -782,7 +823,9 @@ class ViewOrdersIn:
|
|||||||
'trailer_reg_number': self.trailer_reg_number.value,
|
'trailer_reg_number': self.trailer_reg_number.value,
|
||||||
'received_price': self.received_price.value,
|
'received_price': self.received_price.value,
|
||||||
'loading_addresses': loading_addresses,
|
'loading_addresses': loading_addresses,
|
||||||
'unloading_addresses': unloading_addresses
|
'unloading_addresses': unloading_addresses,
|
||||||
|
'file':self.filename.value,
|
||||||
|
'expenses': self.expenses.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:
|
||||||
@@ -904,7 +947,9 @@ class ViewOrdersIn:
|
|||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
|
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
|
||||||
self.order_number
|
self.order_number,
|
||||||
|
self.upload_order_btn,
|
||||||
|
self.filename
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
@@ -998,6 +1043,7 @@ class ViewOrdersIn:
|
|||||||
alignment=ft.MainAxisAlignment.START
|
alignment=ft.MainAxisAlignment.START
|
||||||
),
|
),
|
||||||
self.received_price,
|
self.received_price,
|
||||||
|
self.expenses,
|
||||||
],
|
],
|
||||||
expand=2.5
|
expand=2.5
|
||||||
)
|
)
|
||||||
@@ -1037,7 +1083,7 @@ class ViewOrdersIn:
|
|||||||
[
|
[
|
||||||
self.loading_date,
|
self.loading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.DatePicker(
|
ft.DatePicker(
|
||||||
@@ -1054,7 +1100,7 @@ class ViewOrdersIn:
|
|||||||
[
|
[
|
||||||
self.loading_hour,
|
self.loading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1067,7 +1113,7 @@ class ViewOrdersIn:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1130,7 +1176,7 @@ class ViewOrdersIn:
|
|||||||
[
|
[
|
||||||
self.unloading_date,
|
self.unloading_date,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick date",
|
"Select date",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.DatePicker(
|
ft.DatePicker(
|
||||||
@@ -1147,7 +1193,7 @@ class ViewOrdersIn:
|
|||||||
[
|
[
|
||||||
self.unloading_hour,
|
self.unloading_hour,
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - Start",
|
"Select hour - Start",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
@@ -1160,7 +1206,7 @@ class ViewOrdersIn:
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ft.ElevatedButton(
|
ft.ElevatedButton(
|
||||||
"Pick hour - End",
|
"Select hour - End",
|
||||||
icon=ft.Icons.CALENDAR_MONTH,
|
icon=ft.Icons.CALENDAR_MONTH,
|
||||||
on_click=lambda e: self.page.open(
|
on_click=lambda e: self.page.open(
|
||||||
ft.TimePicker(
|
ft.TimePicker(
|
||||||
|
|||||||
@@ -5,13 +5,21 @@ from models.user import Users
|
|||||||
admin_user_bp = Blueprint("admin_user", __name__, url_prefix="/admin/users")
|
admin_user_bp = Blueprint("admin_user", __name__, url_prefix="/admin/users")
|
||||||
|
|
||||||
# Get all users with role "user"
|
# Get all users with role "user"
|
||||||
@admin_user_bp.route("", methods=["GET"])
|
@admin_user_bp.route("/", methods=["GET"])
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
def get_all_users():
|
def get_all_users():
|
||||||
users_model = Users()
|
users_model = Users()
|
||||||
users = users_model.get_all_users_with_role("user")
|
users = users_model.get_all_users_with_role("user")
|
||||||
return jsonify(users), 200
|
return jsonify(users), 200
|
||||||
|
|
||||||
|
# Get all users with role "user"
|
||||||
|
@admin_user_bp.route("/company_users", methods=["GET"])
|
||||||
|
@jwt_required()
|
||||||
|
def get_all_company_users():
|
||||||
|
users_model = Users()
|
||||||
|
users = users_model.get_all_users_with_role("company_user")
|
||||||
|
return jsonify(users), 200
|
||||||
|
|
||||||
# Get a single user by ID
|
# Get a single user by ID
|
||||||
@admin_user_bp.route("/<int:user_id>", methods=["GET"])
|
@admin_user_bp.route("/<int:user_id>", methods=["GET"])
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
@@ -38,3 +46,21 @@ def update_user():
|
|||||||
users_model.update_user(data)
|
users_model.update_user(data)
|
||||||
|
|
||||||
return jsonify({"message": "User updated successfully."}), 200
|
return jsonify({"message": "User updated successfully."}), 200
|
||||||
|
|
||||||
|
# Deactivate
|
||||||
|
@admin_user_bp.route("/deactivate", methods=["POST"])
|
||||||
|
@jwt_required()
|
||||||
|
def deactivate_user():
|
||||||
|
if not request.is_json:
|
||||||
|
print("Content-Type received:", request.content_type)
|
||||||
|
return jsonify({"error": "Invalid content type, must be application/json"}), 415
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
user_id = data.get("user_id")
|
||||||
|
if not user_id:
|
||||||
|
return jsonify({"error": "Missing user_id"}), 400
|
||||||
|
|
||||||
|
users_model = Users()
|
||||||
|
users_model.deactivate_user(user_id)
|
||||||
|
|
||||||
|
return jsonify({"message": "User updated successfully."}), 200
|
||||||
@@ -12,6 +12,7 @@ from routes.report import report_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 apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from models.subscription import Subscription
|
from models.subscription import Subscription
|
||||||
@@ -69,7 +70,7 @@ app.register_blueprint(report_bp, url_prefix="/report")
|
|||||||
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)
|
||||||
|
|
||||||
def update_subscription_statuses_job():
|
def update_subscription_statuses_job():
|
||||||
print("[Scheduler] Running daily subscription status check...")
|
print("[Scheduler] Running daily subscription status check...")
|
||||||
|
|||||||
BIN
transportmanager/server/assets/Manual.pdf
Normal file
BIN
transportmanager/server/assets/Manual.pdf
Normal file
Binary file not shown.
81
transportmanager/server/models/company_users.py
Normal file
81
transportmanager/server/models/company_users.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from database import get_connection, is_postgres
|
||||||
|
|
||||||
|
class CompanyUsers:
|
||||||
|
def __init__(self):
|
||||||
|
self.ph = "%s" if is_postgres() else "?"
|
||||||
|
|
||||||
|
def access_to_dict(self, row):
|
||||||
|
access = {
|
||||||
|
'id': row[0],
|
||||||
|
'company_user_id': row[1],
|
||||||
|
'clients': row[2],
|
||||||
|
'transporters': row[3],
|
||||||
|
'destinations': row[4],
|
||||||
|
'orders_in': row[5],
|
||||||
|
'orders_out': row[6],
|
||||||
|
'report':row[7],
|
||||||
|
'created_at': row[8]
|
||||||
|
}
|
||||||
|
return access
|
||||||
|
|
||||||
|
def insert_company_user_access(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 company_user_access (
|
||||||
|
company_user_id, clients, transporters, destinations, orders_in, orders_out, report, created_at
|
||||||
|
) VALUES ({self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}, {self.ph}) {returning}
|
||||||
|
"""
|
||||||
|
cursor.execute(
|
||||||
|
query,
|
||||||
|
(
|
||||||
|
access_data['company_user_id'],
|
||||||
|
access_data['clients'],
|
||||||
|
access_data['transporters'],
|
||||||
|
access_data['destinations'],
|
||||||
|
access_data['orders_in'],
|
||||||
|
access_data['orders_out'],
|
||||||
|
access_data['report'],
|
||||||
|
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_access_by_company_user_id(self, company_user_id):
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(f"SELECT * FROM company_user_access WHERE company_user_id = {self.ph}", (company_user_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return self.access_to_dict(row) if row else None
|
||||||
|
|
||||||
|
def update_company_user_access(self, data):
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE company_user_access
|
||||||
|
SET clients = {self.ph}, transporters = {self.ph}, destinations = {self.ph}, orders_in = {self.ph}, orders_out = {self.ph}, report = {self.ph}
|
||||||
|
WHERE company_user_id = {self.ph}
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
data['clients'],
|
||||||
|
data['transporters'],
|
||||||
|
data['destinations'],
|
||||||
|
data['orders_in'],
|
||||||
|
data['orders_out'],
|
||||||
|
data['report'],
|
||||||
|
data['company_user_id']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if hasattr(conn, "commit"):
|
||||||
|
conn.commit()
|
||||||
@@ -19,6 +19,8 @@ class OrdersIn:
|
|||||||
"trailer_reg_number": row[8],
|
"trailer_reg_number": row[8],
|
||||||
"received_price": row[9],
|
"received_price": row[9],
|
||||||
"created_at": row[10],
|
"created_at": row[10],
|
||||||
|
"file":row[11],
|
||||||
|
"expenses": row[12],
|
||||||
}
|
}
|
||||||
|
|
||||||
def order_point_to_dict(self, row):
|
def order_point_to_dict(self, row):
|
||||||
@@ -41,8 +43,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)
|
ldb_quantity, kg_quantity, track_reg_number, trailer_reg_number, created_at, file_name, expenses)
|
||||||
VALUES ({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}){returning}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
data["user_id"],
|
data["user_id"],
|
||||||
@@ -55,6 +57,8 @@ class OrdersIn:
|
|||||||
data["track_reg_number"],
|
data["track_reg_number"],
|
||||||
data["trailer_reg_number"],
|
data["trailer_reg_number"],
|
||||||
created_at,
|
created_at,
|
||||||
|
data["file"],
|
||||||
|
data["expenses"]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
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)
|
||||||
@@ -71,7 +75,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}
|
trailer_reg_number = {self.ph}, file_name = {self.ph}, expenses = {self.ph}
|
||||||
WHERE id = {self.ph}
|
WHERE id = {self.ph}
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
@@ -84,6 +88,8 @@ class OrdersIn:
|
|||||||
data["kg_quantity"],
|
data["kg_quantity"],
|
||||||
data["track_reg_number"],
|
data["track_reg_number"],
|
||||||
data["trailer_reg_number"],
|
data["trailer_reg_number"],
|
||||||
|
data['file'],
|
||||||
|
data['expenses'],
|
||||||
data["id"],
|
data["id"],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ class Users:
|
|||||||
'created_at': row[12],
|
'created_at': row[12],
|
||||||
'otp_code': row[13],
|
'otp_code': row[13],
|
||||||
'otp_expiration': row[14],
|
'otp_expiration': row[14],
|
||||||
'user_role': row[15]
|
'user_role': row[15],
|
||||||
|
'company_id': row[16],
|
||||||
|
'active': row[17],
|
||||||
|
'temporary_password': row[18]
|
||||||
}
|
}
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@@ -71,6 +74,28 @@ class Users:
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
return inserted_id
|
return inserted_id
|
||||||
|
|
||||||
|
def insert_company_user(self, name, email, password_hash, company_id):
|
||||||
|
created_at = datetime.now().isoformat()
|
||||||
|
company_id = company_id
|
||||||
|
user_role = 'company_user'
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
returning = "RETURNING id" if is_postgres() else ""
|
||||||
|
query = f"""
|
||||||
|
INSERT INTO users (
|
||||||
|
name, email, password_hash, created_at, user_role, company_id
|
||||||
|
) VALUES ({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))
|
||||||
|
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 update_user_otp(self, user_id, otp_code, expiration):
|
def update_user_otp(self, user_id, otp_code, expiration):
|
||||||
with get_connection() as conn:
|
with get_connection() as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
@@ -192,3 +217,31 @@ class Users:
|
|||||||
)
|
)
|
||||||
if hasattr(conn, "commit"):
|
if hasattr(conn, "commit"):
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
def deactivate_user(self, user_id):
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE users
|
||||||
|
SET active = {self.ph}
|
||||||
|
WHERE id = {self.ph}
|
||||||
|
""",
|
||||||
|
(0, user_id)
|
||||||
|
)
|
||||||
|
if hasattr(conn, "commit"):
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def update_temp_pass(self, user_id):
|
||||||
|
with get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
f"""
|
||||||
|
UPDATE users
|
||||||
|
SET temporary_passwrd = {self.ph}
|
||||||
|
WHERE id = {self.ph}
|
||||||
|
""",
|
||||||
|
(0, user_id)
|
||||||
|
)
|
||||||
|
if hasattr(conn, "commit"):
|
||||||
|
conn.commit()
|
||||||
@@ -9,6 +9,7 @@ import os
|
|||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
|
|
||||||
from models.user import Users
|
from models.user import Users
|
||||||
|
from utils.welcome_email import WelcomeMessage
|
||||||
|
|
||||||
auth_bp = Blueprint("auth", __name__)
|
auth_bp = Blueprint("auth", __name__)
|
||||||
|
|
||||||
@@ -30,8 +31,10 @@ def register():
|
|||||||
password_hash = generate_password_hash(password)
|
password_hash = generate_password_hash(password)
|
||||||
users.insert_user(name, email, password_hash)
|
users.insert_user(name, email, password_hash)
|
||||||
|
|
||||||
return jsonify({"message": "User registered successfully!"}), 201
|
welcome_message = WelcomeMessage(name, email)
|
||||||
|
welcome_message.send_email()
|
||||||
|
|
||||||
|
return jsonify({"message": "User registered successfully!"}), 201
|
||||||
|
|
||||||
@auth_bp.route("/login", methods=["POST"])
|
@auth_bp.route("/login", methods=["POST"])
|
||||||
def login():
|
def login():
|
||||||
@@ -47,6 +50,9 @@ def login():
|
|||||||
if not user or not check_password_hash(user["password_hash"], password):
|
if not user or not check_password_hash(user["password_hash"], password):
|
||||||
return jsonify({"error": "Invalid credentials"}), 401
|
return jsonify({"error": "Invalid credentials"}), 401
|
||||||
|
|
||||||
|
if user["active"] != 1:
|
||||||
|
return jsonify({"error": "Inactive user"}), 401
|
||||||
|
|
||||||
otp_code = str(random.randint(100000, 999999))
|
otp_code = str(random.randint(100000, 999999))
|
||||||
expiration = datetime.datetime.now(timezone.utc) + datetime.timedelta(minutes=10)
|
expiration = datetime.datetime.now(timezone.utc) + datetime.timedelta(minutes=10)
|
||||||
users.update_user_otp(user["id"], otp_code, expiration)
|
users.update_user_otp(user["id"], otp_code, expiration)
|
||||||
@@ -188,7 +194,8 @@ def me():
|
|||||||
"terms": user["terms"],
|
"terms": user["terms"],
|
||||||
"first_order_number": user["first_order_number"],
|
"first_order_number": user["first_order_number"],
|
||||||
"created_at": user["created_at"],
|
"created_at": user["created_at"],
|
||||||
"user_role": user["user_role"]
|
"user_role": user["user_role"],
|
||||||
|
"temporary_password": user["temporary_password"]
|
||||||
}), 200
|
}), 200
|
||||||
|
|
||||||
|
|
||||||
@@ -202,3 +209,16 @@ def validate_token():
|
|||||||
if not user:
|
if not user:
|
||||||
return jsonify({"error": "User not found"}), 404
|
return jsonify({"error": "User not found"}), 404
|
||||||
return jsonify({"message": "Token is valid"}), 200
|
return jsonify({"message": "Token is valid"}), 200
|
||||||
|
|
||||||
|
@auth_bp.route("/temporary_password", methods=["POST"])
|
||||||
|
@jwt_required()
|
||||||
|
def change_passwd():
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
return jsonify({"error": "Password not found"}), 404
|
||||||
|
users = Users()
|
||||||
|
user_id = get_jwt_identity()
|
||||||
|
new_password_hash = generate_password_hash(data['password'])
|
||||||
|
users.update_user_password(user_id, new_password_hash)
|
||||||
|
users.update_temp_pass(user_id)
|
||||||
|
return jsonify({"message": "Password has been updated successfully."}), 200
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from models.client import Clients
|
from models.client import Clients
|
||||||
|
from models.user import Users
|
||||||
|
|
||||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
|
|
||||||
@@ -10,6 +11,10 @@ clients_bp = Blueprint("clients", __name__, url_prefix="/clients")
|
|||||||
def list_clients():
|
def list_clients():
|
||||||
clients_db = Clients()
|
clients_db = Clients()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
clients = clients_db.get_all_by_user(user_id)
|
clients = clients_db.get_all_by_user(user_id)
|
||||||
return jsonify(clients), 200
|
return jsonify(clients), 200
|
||||||
|
|
||||||
@@ -19,6 +24,10 @@ def create_client():
|
|||||||
clients_db = Clients()
|
clients_db = Clients()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
client_id = clients_db.create(
|
client_id = clients_db.create(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
name=data["name"],
|
name=data["name"],
|
||||||
|
|||||||
118
transportmanager/server/routes/company_user.py
Normal file
118
transportmanager/server/routes/company_user.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from models.user import Users
|
||||||
|
from utils.welcome_email import WelcomeMessage
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
|
from models.company_users import CompanyUsers
|
||||||
|
|
||||||
|
company_user_bp = Blueprint("company_user", __name__, url_prefix="/company_user")
|
||||||
|
|
||||||
|
@company_user_bp.route('/access/<int:id>', methods=["GET"])
|
||||||
|
@jwt_required()
|
||||||
|
def get_access(id):
|
||||||
|
company_user_id = id
|
||||||
|
if not company_user_id:
|
||||||
|
return jsonify({"error":"Missing required fields"}), 400
|
||||||
|
|
||||||
|
company_users = CompanyUsers()
|
||||||
|
access = company_users.get_access_by_company_user_id(company_user_id)
|
||||||
|
return jsonify(access)
|
||||||
|
|
||||||
|
@company_user_bp.route("/register_company_user", methods=["POST"])
|
||||||
|
@jwt_required()
|
||||||
|
def register_company_user():
|
||||||
|
users = Users()
|
||||||
|
data = request.get_json()
|
||||||
|
name = data.get("name")
|
||||||
|
email = data.get("email")
|
||||||
|
company_id = data.get("company_id")
|
||||||
|
password = data.get("password")
|
||||||
|
|
||||||
|
if not name or not email or not password or not company_id:
|
||||||
|
return jsonify({"error": "Missing required fields"}), 400
|
||||||
|
|
||||||
|
existing_user = users.get_user_by_email(email)
|
||||||
|
if existing_user:
|
||||||
|
return jsonify({"error": "User already exists"}), 409
|
||||||
|
|
||||||
|
password_hash = generate_password_hash(password)
|
||||||
|
company_user_id = users.insert_company_user(name, email, password_hash, company_id)
|
||||||
|
|
||||||
|
welcome_message = WelcomeMessage(name, email)
|
||||||
|
welcome_message.send_email()
|
||||||
|
|
||||||
|
return jsonify({"message": "User registered successfully!", "company_user_id":company_user_id}), 201
|
||||||
|
|
||||||
|
@company_user_bp.route('/access', methods=["POST"])
|
||||||
|
@jwt_required()
|
||||||
|
def create_access():
|
||||||
|
data = request.get_json()
|
||||||
|
company_user_id = data.get('company_user_id')
|
||||||
|
orders_in = data.get("orders_in")
|
||||||
|
orders_out = data.get("orders_out")
|
||||||
|
addresses = data.get("addresses")
|
||||||
|
clients = data.get("clients")
|
||||||
|
transporters = data.get("transporters")
|
||||||
|
report = data.get("report")
|
||||||
|
company_users = CompanyUsers()
|
||||||
|
# Use explicit None checks so that False/0 values are allowed
|
||||||
|
if (
|
||||||
|
company_user_id is None or
|
||||||
|
orders_in is None or
|
||||||
|
orders_out is None or
|
||||||
|
addresses is None or
|
||||||
|
clients is None or
|
||||||
|
transporters is None or
|
||||||
|
report is None
|
||||||
|
):
|
||||||
|
return jsonify({"error": "Missing required fields"}), 400
|
||||||
|
|
||||||
|
access_data = {
|
||||||
|
'company_user_id': company_user_id,
|
||||||
|
'clients': clients,
|
||||||
|
'transporters': transporters,
|
||||||
|
'destinations': addresses,
|
||||||
|
'orders_in': orders_in,
|
||||||
|
'orders_out': orders_out,
|
||||||
|
'report': report
|
||||||
|
}
|
||||||
|
company_users.insert_company_user_access(access_data)
|
||||||
|
|
||||||
|
return jsonify({"message": "Company User access inserted!"})
|
||||||
|
|
||||||
|
@company_user_bp.route('/update_access', methods=["PUT"])
|
||||||
|
@jwt_required()
|
||||||
|
def update_access():
|
||||||
|
data = request.get_json()
|
||||||
|
company_user_id = data.get('company_user_id')
|
||||||
|
orders_in = data.get("orders_in")
|
||||||
|
orders_out = data.get("orders_out")
|
||||||
|
addresses = data.get("addresses")
|
||||||
|
clients = data.get("clients")
|
||||||
|
transporters = data.get("transporters")
|
||||||
|
report = data.get("report")
|
||||||
|
company_users = CompanyUsers()
|
||||||
|
# Use explicit None checks so that False/0 values are allowed
|
||||||
|
if (
|
||||||
|
company_user_id is None or
|
||||||
|
orders_in is None or
|
||||||
|
orders_out is None or
|
||||||
|
addresses is None or
|
||||||
|
clients is None or
|
||||||
|
transporters is None or
|
||||||
|
report is None
|
||||||
|
):
|
||||||
|
return jsonify({"error": "Missing required fields"}), 400
|
||||||
|
|
||||||
|
access_data = {
|
||||||
|
'company_user_id': company_user_id,
|
||||||
|
'clients': clients,
|
||||||
|
'transporters': transporters,
|
||||||
|
'destinations': addresses,
|
||||||
|
'orders_in': orders_in,
|
||||||
|
'orders_out': orders_out,
|
||||||
|
'report': report
|
||||||
|
}
|
||||||
|
company_users.update_company_user_access(access_data)
|
||||||
|
|
||||||
|
return jsonify({"message": "Company User access updated!"})
|
||||||
@@ -2,6 +2,7 @@ from flask import Blueprint, request, jsonify
|
|||||||
from models.destinations import Destinations
|
from models.destinations import Destinations
|
||||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
from utils.maps import AdressCoordinates
|
from utils.maps import AdressCoordinates
|
||||||
|
from models.user import Users
|
||||||
|
|
||||||
destinations_bp = Blueprint("destinations", __name__, url_prefix="/destinations")
|
destinations_bp = Blueprint("destinations", __name__, url_prefix="/destinations")
|
||||||
|
|
||||||
@@ -10,6 +11,10 @@ destinations_bp = Blueprint("destinations", __name__, url_prefix="/destinations"
|
|||||||
def list_destinations():
|
def list_destinations():
|
||||||
destinations_db = Destinations()
|
destinations_db = Destinations()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
destinations = destinations_db.get_all_by_user(user_id)
|
destinations = destinations_db.get_all_by_user(user_id)
|
||||||
return jsonify([dict(d) for d in destinations]), 200
|
return jsonify([dict(d) for d in destinations]), 200
|
||||||
|
|
||||||
@@ -18,6 +23,10 @@ def list_destinations():
|
|||||||
def create_destination():
|
def create_destination():
|
||||||
destinations_db = Destinations()
|
destinations_db = Destinations()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
destination_id = destinations_db.create(user_id, data.get("name"), data.get("address"))
|
destination_id = destinations_db.create(user_id, data.get("name"), data.get("address"))
|
||||||
# coordinates = AdressCoordinates(data.get("address"))
|
# coordinates = AdressCoordinates(data.get("address"))
|
||||||
@@ -33,6 +42,10 @@ def create_destination():
|
|||||||
def update_destination(id):
|
def update_destination(id):
|
||||||
destinations_db = Destinations()
|
destinations_db = Destinations()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
destinations_db.update(id, user_id, data.get("name"), data.get("address"))
|
destinations_db.update(id, user_id, data.get("name"), data.get("address"))
|
||||||
coordinates = AdressCoordinates(data.get("address"))
|
coordinates = AdressCoordinates(data.get("address"))
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ orders_bp = Blueprint("orders", __name__, url_prefix="/orders")
|
|||||||
@jwt_required()
|
@jwt_required()
|
||||||
def create_order_route():
|
def create_order_route():
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
orders = OrdersOut()
|
orders = OrdersOut()
|
||||||
incoming_data = request.json
|
incoming_data = request.json
|
||||||
#here we need to first implement the order pdf
|
#here we need to first implement the order pdf
|
||||||
@@ -83,6 +87,10 @@ def update_order_route(order_id):
|
|||||||
orders = OrdersOut()
|
orders = OrdersOut()
|
||||||
data = request.json
|
data = request.json
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
if not order:
|
if not order:
|
||||||
return jsonify({"error": "Order not found"}), 404
|
return jsonify({"error": "Order not found"}), 404
|
||||||
@@ -150,6 +158,10 @@ def update_order_route(order_id):
|
|||||||
def delete_order_route(order_id):
|
def delete_order_route(order_id):
|
||||||
orders = OrdersOut()
|
orders = OrdersOut()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
if not order:
|
if not order:
|
||||||
return jsonify({"error": "Order not found"}), 404
|
return jsonify({"error": "Order not found"}), 404
|
||||||
@@ -168,6 +180,10 @@ def delete_order_route(order_id):
|
|||||||
def list_orders():
|
def list_orders():
|
||||||
orders = OrdersOut()
|
orders = OrdersOut()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
try:
|
try:
|
||||||
user_orders = orders.get_orders_by_user(user_id)
|
user_orders = orders.get_orders_by_user(user_id)
|
||||||
#result = [{"id": order["id"], "order_number": order["order_number"]} for order in user_orders]
|
#result = [{"id": order["id"], "order_number": order["order_number"]} for order in user_orders]
|
||||||
@@ -180,6 +196,10 @@ def list_orders():
|
|||||||
def get_order(order_id):
|
def get_order(order_id):
|
||||||
orders = OrdersOut()
|
orders = OrdersOut()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
points = orders.get_order_points_by_order(order['id'])
|
points = orders.get_order_points_by_order(order['id'])
|
||||||
loading_points = []
|
loading_points = []
|
||||||
@@ -251,6 +271,10 @@ def cancel_order(order_id):
|
|||||||
orders = OrdersOut()
|
orders = OrdersOut()
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
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'
|
||||||
cancel_order_pdf(pdf_name)
|
cancel_order_pdf(pdf_name)
|
||||||
orders.cancel_order(order_id)
|
orders.cancel_order(order_id)
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify, abort
|
||||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
from models.order_in import OrdersIn
|
from models.order_in import OrdersIn
|
||||||
from models.transporters import Transporters
|
from models.transporters import Transporters
|
||||||
from models.user import Users
|
from models.user import Users
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
from flask import send_from_directory
|
||||||
|
import mimetypes
|
||||||
|
|
||||||
orders_in_bp = Blueprint("orders_in", __name__, url_prefix="/orders_in")
|
orders_in_bp = Blueprint("orders_in", __name__, url_prefix="/orders_in")
|
||||||
|
|
||||||
@@ -11,6 +14,10 @@ orders_in_bp = Blueprint("orders_in", __name__, url_prefix="/orders_in")
|
|||||||
@jwt_required()
|
@jwt_required()
|
||||||
def create_order_in_route():
|
def create_order_in_route():
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
orders = OrdersIn()
|
orders = OrdersIn()
|
||||||
incoming_data = request.json
|
incoming_data = request.json
|
||||||
try:
|
try:
|
||||||
@@ -25,7 +32,10 @@ def create_order_in_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"],
|
||||||
|
'file': incoming_data['file'],
|
||||||
|
'expenses': incoming_data['expenses']
|
||||||
}
|
}
|
||||||
|
#print(order_data)
|
||||||
order_id = orders.create_order(order_data)
|
order_id = orders.create_order(order_data)
|
||||||
|
|
||||||
for address in incoming_data["loading_addresses"]:
|
for address in incoming_data["loading_addresses"]:
|
||||||
@@ -60,6 +70,10 @@ def update_order_route(order_id):
|
|||||||
orders = OrdersIn()
|
orders = OrdersIn()
|
||||||
data = request.json
|
data = request.json
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
if not order:
|
if not order:
|
||||||
return jsonify({"error": "Order in not found"}), 404
|
return jsonify({"error": "Order in not found"}), 404
|
||||||
@@ -77,6 +91,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"]),
|
||||||
|
'file': data.get("file", order["file"]),
|
||||||
|
'expenses': data.get("expenses", order["expenses"]),
|
||||||
"user_id":user_id
|
"user_id":user_id
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -113,6 +129,10 @@ def update_order_route(order_id):
|
|||||||
def delete_order_route(order_id):
|
def delete_order_route(order_id):
|
||||||
orders = OrdersIn()
|
orders = OrdersIn()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
if not order:
|
if not order:
|
||||||
return jsonify({"error": "Order in not found"}), 404
|
return jsonify({"error": "Order in not found"}), 404
|
||||||
@@ -131,6 +151,10 @@ def delete_order_route(order_id):
|
|||||||
def list_orders():
|
def list_orders():
|
||||||
orders = OrdersIn()
|
orders = OrdersIn()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
try:
|
try:
|
||||||
user_orders = orders.get_orders_by_user(user_id)
|
user_orders = orders.get_orders_by_user(user_id)
|
||||||
#result = [{"id": order["id"], "order_number": order["order_number"]} for order in user_orders]
|
#result = [{"id": order["id"], "order_number": order["order_number"]} for order in user_orders]
|
||||||
@@ -143,6 +167,10 @@ def list_orders():
|
|||||||
def get_order(order_id):
|
def get_order(order_id):
|
||||||
orders = OrdersIn()
|
orders = OrdersIn()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
order = orders.get_order_by_id(order_id)
|
order = orders.get_order_by_id(order_id)
|
||||||
points = orders.get_order_points_by_order(order['id'])
|
points = orders.get_order_points_by_order(order['id'])
|
||||||
loading_points = []
|
loading_points = []
|
||||||
@@ -161,3 +189,25 @@ def get_order(order_id):
|
|||||||
if order["user_id"] != int(user_id):
|
if order["user_id"] != int(user_id):
|
||||||
return jsonify({"error": "Unauthorized"}), 403
|
return jsonify({"error": "Unauthorized"}), 403
|
||||||
return jsonify(order), 200
|
return jsonify(order), 200
|
||||||
|
|
||||||
|
@orders_in_bp.route("/files/<path:filename>", methods=["GET"])
|
||||||
|
#@jwt_required()
|
||||||
|
def serve_order_pdf(filename):
|
||||||
|
try:
|
||||||
|
# Directory containing uploaded client files
|
||||||
|
uploads_dir = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "..", "..", "client", "uploads")
|
||||||
|
)
|
||||||
|
# Security: prevent path traversal and ensure file exists
|
||||||
|
abs_file_path = os.path.abspath(os.path.join(uploads_dir, filename))
|
||||||
|
if not abs_file_path.startswith(uploads_dir + os.sep):
|
||||||
|
abort(404)
|
||||||
|
if not os.path.isfile(abs_file_path):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
guessed_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
||||||
|
# send_from_directory expects the directory and the filename relative to it
|
||||||
|
return send_from_directory(uploads_dir, filename, mimetype=guessed_type, as_attachment=False)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return jsonify({"error": "File not found"}), 404
|
||||||
@@ -2,6 +2,7 @@ from flask import Blueprint, request, jsonify
|
|||||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
from models.order_out import OrdersOut # Your plain SQL model
|
from models.order_out import OrdersOut # Your plain SQL model
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from models.user import Users
|
||||||
|
|
||||||
report_bp = Blueprint("report", __name__, url_prefix="/report")
|
report_bp = Blueprint("report", __name__, url_prefix="/report")
|
||||||
|
|
||||||
@@ -10,6 +11,10 @@ report_bp = Blueprint("report", __name__, url_prefix="/report")
|
|||||||
def get_profit_report():
|
def get_profit_report():
|
||||||
try:
|
try:
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
# Get filters from query params
|
# Get filters from query params
|
||||||
date_from = request.args.get("date_from")
|
date_from = request.args.get("date_from")
|
||||||
date_to = request.args.get("date_to")
|
date_to = request.args.get("date_to")
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ subscription_bp = Blueprint("subscription", __name__, url_prefix="/subscription"
|
|||||||
@jwt_required()
|
@jwt_required()
|
||||||
def get_subscription():
|
def get_subscription():
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
subscription_model = Subscription()
|
subscription_model = Subscription()
|
||||||
subscriptions = subscription_model.get_by_user_id(user_id)
|
subscriptions = subscription_model.get_by_user_id(user_id)
|
||||||
return jsonify(subscriptions), 200
|
return jsonify(subscriptions), 200
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||||
from models.transporters import Transporters
|
from models.transporters import Transporters
|
||||||
|
from models.user import Users
|
||||||
|
|
||||||
transporters_bp = Blueprint("transporters", __name__, url_prefix="/transporters")
|
transporters_bp = Blueprint("transporters", __name__, url_prefix="/transporters")
|
||||||
|
|
||||||
@@ -8,6 +9,10 @@ transporters_bp = Blueprint("transporters", __name__, url_prefix="/transporters"
|
|||||||
@jwt_required()
|
@jwt_required()
|
||||||
def list_transporters():
|
def list_transporters():
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
transporters_db = Transporters()
|
transporters_db = Transporters()
|
||||||
transporters = transporters_db.get_all_transporters_by_user(user_id)
|
transporters = transporters_db.get_all_transporters_by_user(user_id)
|
||||||
return jsonify(transporters), 200
|
return jsonify(transporters), 200
|
||||||
@@ -18,6 +23,10 @@ def create_transporter():
|
|||||||
transporters_db = Transporters()
|
transporters_db = Transporters()
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
transporter_id = transporters_db.create_transporter(
|
transporter_id = transporters_db.create_transporter(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
name=data.get("name"),
|
name=data.get("name"),
|
||||||
@@ -36,6 +45,10 @@ def create_transporter():
|
|||||||
def update_transporter(transporter_id):
|
def update_transporter(transporter_id):
|
||||||
transporters_db = Transporters()
|
transporters_db = Transporters()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||||
if not transporter:
|
if not transporter:
|
||||||
@@ -58,6 +71,10 @@ def update_transporter(transporter_id):
|
|||||||
def delete_transporter(transporter_id):
|
def delete_transporter(transporter_id):
|
||||||
transporters_db = Transporters()
|
transporters_db = Transporters()
|
||||||
user_id = get_jwt_identity()
|
user_id = get_jwt_identity()
|
||||||
|
users = Users()
|
||||||
|
user = users.get_user_by_id(user_id)
|
||||||
|
if user['user_role'] == 'company_user':
|
||||||
|
user_id = user['company_id']
|
||||||
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||||
if not transporter:
|
if not transporter:
|
||||||
return jsonify({"error": "Transporter not found"}), 404
|
return jsonify({"error": "Transporter not found"}), 404
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ CREATE TABLE IF NOT EXISTS users (
|
|||||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
otp_code TEXT,
|
otp_code TEXT,
|
||||||
otp_expiration TIMESTAMPTZ,
|
otp_expiration TIMESTAMPTZ,
|
||||||
user_role TEXT NOT NULL DEFAULT 'user' CHECK (user_role IN ('user', 'admin'))
|
user_role TEXT NOT NULL DEFAULT 'user' CHECK (user_role IN ('user', 'admin', 'company_user')),
|
||||||
|
company_id INTEGER DEFAULT 0,
|
||||||
|
active INTEGER DEFAULT 1,
|
||||||
|
temporary_passwrd INTEGER DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Clients table
|
-- Clients table
|
||||||
@@ -94,6 +97,8 @@ CREATE TABLE IF NOT EXISTS orders_in (
|
|||||||
trailer_reg_number TEXT,
|
trailer_reg_number TEXT,
|
||||||
received_price DOUBLE PRECISION,
|
received_price DOUBLE PRECISION,
|
||||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
file_name TEXT,
|
||||||
|
expenses DOUBLE PRECISION,
|
||||||
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
|
||||||
);
|
);
|
||||||
@@ -144,3 +149,16 @@ CREATE TABLE IF NOT EXISTS email (
|
|||||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS company_user_access (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
company_user_id INTEGER NOT NULL,
|
||||||
|
clients INTEGER,
|
||||||
|
transporters INTEGER,
|
||||||
|
destinations INTEGER,
|
||||||
|
orders_in INTEGER,
|
||||||
|
orders_out INTEGER,
|
||||||
|
report INTEGER,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY(company_user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|||||||
@@ -82,9 +82,9 @@ def send_gmail(to_email, subject, body):
|
|||||||
def send_gmail_with_attachment(to_email, subject, body, attachment_path):
|
def send_gmail_with_attachment(to_email, subject, body, attachment_path):
|
||||||
smtp_host = "smtp.gmail.com"
|
smtp_host = "smtp.gmail.com"
|
||||||
smtp_port = 587
|
smtp_port = 587
|
||||||
smtp_user = os.environ.get("GMAIL_USER")
|
smtp_user = 'macamete.robert@gmail.com'
|
||||||
smtp_pass = os.environ.get("GMAIL_PASS")
|
smtp_pass = 'advx yqlv jkaa czvr'
|
||||||
sender_email = smtp_user
|
sender_email = 'macamete.robert@gmail.com'
|
||||||
|
|
||||||
if not all([smtp_user, smtp_pass]):
|
if not all([smtp_user, smtp_pass]):
|
||||||
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||||
|
|||||||
31
transportmanager/server/utils/welcome_email.py
Normal file
31
transportmanager/server/utils/welcome_email.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import os
|
||||||
|
from utils.email import send_gmail_with_attachment
|
||||||
|
|
||||||
|
class WelcomeMessage:
|
||||||
|
def __init__(self, name, email):
|
||||||
|
self.name = name
|
||||||
|
self.email = email
|
||||||
|
self.subject = 'Welcome to Order Go - TMS - Your Account is Ready'
|
||||||
|
self.assets_folder = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "..", "assets")
|
||||||
|
)
|
||||||
|
self.manual = os.path.join(self.assets_folder, "manual.pdf")
|
||||||
|
self.body = f'''
|
||||||
|
|
||||||
|
Dear {self.name},
|
||||||
|
|
||||||
|
We are pleased to welcome you to Order Go - TMS. Thank you for choosing our platform to support your business needs.
|
||||||
|
|
||||||
|
To assist you in getting started, we have attached the User Manual to this email. It provides step-by-step instructions on account setup, feature overview, and best practices for using Order Go - TMS efficiently.
|
||||||
|
|
||||||
|
We recommend reviewing the manual at your convenience to become familiar with the system's capabilities. Should you require any further assistance, our support team is available at support@ordergotms.com.
|
||||||
|
|
||||||
|
We look forward to supporting your success and building a long-term partnership.
|
||||||
|
|
||||||
|
Sincerely,
|
||||||
|
The Order Go - TMS Team
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
def send_email(self):
|
||||||
|
send_gmail_with_attachment(to_email=self.email, subject=self.subject, body=self.body, attachment_path=self.manual)
|
||||||
Reference in New Issue
Block a user