Compare commits

...

22 Commits

Author SHA1 Message Date
4aa36bb5b2 fix admin clients 2025-11-08 16:16:29 +02:00
f05401c493 fix display issues 2025-11-06 18:20:33 +02:00
29cf5925cc fix display issues 2025-11-06 18:15:51 +02:00
574c1227c1 set padding 2025-11-06 17:44:57 +02:00
b77aa0f0d7 add about us 2025-11-06 17:39:35 +02:00
d29b5e9015 add admin settings menu 2025-11-06 13:08:44 +02:00
c54b7bfe78 fix name not show issue 2025-11-06 11:24:04 +02:00
800a5ea258 fix name issue 2025-11-06 11:05:32 +02:00
43a318a1b1 fix login issue 2025-11-06 10:55:45 +02:00
6c713171ed add netopia payment process 2025-11-06 10:48:57 +02:00
5a40af5434 add card de reducere 2025-11-03 17:30:09 +02:00
8a2bad78fa implement delete order 2025-11-02 18:41:12 +02:00
025236013f fix search issue 2025-11-02 18:05:44 +02:00
4fe190ddad add fidelity card module 2025-11-02 18:03:19 +02:00
ee4913487a limit popular products size 2025-10-31 19:28:09 +02:00
14c827af40 add imege to product list 2025-10-30 21:56:46 +02:00
17e859bfd0 add imege to product list 2025-10-30 21:54:36 +02:00
62675c4df7 add search bar 2025-10-30 21:37:42 +02:00
9e08497300 disable cart payment 2025-10-27 22:46:22 +02:00
d1b9855d9c fix search: 2025-10-27 22:29:22 +02:00
d598eaa134 fix similar products 2025-10-27 22:26:02 +02:00
deef3a5c0f fix similar products images 2025-10-27 22:23:54 +02:00
36 changed files with 1945 additions and 151 deletions

View File

@@ -1,3 +1,7 @@
SUPERUSER_EMAIL=macamete.robert@gmail.com NETOPIA_API_KEY=yPYOnzSeU_qdMV5RaMEwvFCkBuQU6PUPijAWja8vY34JuG5SMTLtF7LkcXj8
SUPERUSER_PASSWORD=Inteligent1_eu NETOPIA_POS_SIGNATURE=351D-XRIS-7K5Y-WUZB-Y3IW
SUPERUSER_ROLE=admin NETOPIA_PUBLIC_KEY=-----BEGIN CERTIFICATE-----\nMIIC3zCCAkigAwIBAgIBATANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCUk8xEjAQBgNVBAgTCUJ1Y2hhcmVzdDESMBAGA1UEBxMJQnVjaGFyZXN0MRAwDgYDVQQKEwdORVRPUElBMSEwHwYDVQQLExhORVRPUElBIERldmVsb3BtZW50IHRlYW0xHDAaBgNVBAMTE25ldG9waWEtcGF5bWVudHMucm8wHhcNMjUxMTA0MTUwNTI2WhcNMzUxMTAyMTUwNTI2WjCBiDELMAkGA1UEBhMCUk8xEjAQBgNVBAgTCUJ1Y2hhcmVzdDESMBAGA1UEBxMJQnVjaGFyZXN0MRAwDgYDVQQKEwdORVRPUElBMSEwHwYDVQQLExhORVRPUElBIERldmVsb3BtZW50IHRlYW0xHDAaBgNVBAMTE25ldG9waWEtcGF5bWVudHMucm8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALwh0/NhEpZFuKvghZ9N75CXba05MWNCh422kcfFKbqP5YViCUBg3Mc5ZYd1e0Xi9Ui1QI2Z/jvvchrDZGQwjarApr3S9bowHEkZH81ZolOoPHBZbYpA28BIyHYRcaTXjLtiBGvjpwuzljmXeBoVLinIaE0IUpMen9MLWG2fGMddAgMBAAGjVzBVMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQ9yXChMGxzUzQflmkXT1oyIBoetTANBgkqhkiG9w0BAQsFAAOBgQCSxX3hnoP+vWmdecz/Oustubp4Q89fV/bGMBztQy8QbnMjKghUKAba/0CGs2MbN2bWCN78mOak8Oi6qNB7/Z6/yKWTJdkwYjXl/C6UQhZ7k11XKINGk3LD9wTiyKsWt1iioNjNN6h8Fqeh4So5ikEzZG7LnvbAz+ct9YBjyHiCLw==\n-----END CERTIFICATE-----
NETOPIA_REDIRECT_URL=https://www.tainagustului.ro/payment/redirect
NETOPIA_NOTIFY_URL=https://www.tainagustului.ro/api/payments/ipn
NETOPIA_CANCEL_URL=https://www.tainagustului.ro/cos
NETOPIA_IS_LIVE=false

View File

@@ -8,10 +8,19 @@ class Clients:
self.user_manager = Users() self.user_manager = Users()
self.company_manager = Company() self.company_manager = Company()
self.user_name = ft.TextField(label="Nume si Prenume") self.first_name = ft.TextField(label="Prenume")
self.last_name = ft.TextField(label="Nume")
self.email = ft.TextField(label="E-mail") self.email = ft.TextField(label="E-mail")
self.phone = ft.TextField(label="Telefon") self.phone = ft.TextField(label="Telefon")
self.address = ft.TextField(label="Adresa", multiline=True, min_lines=3, max_lines=5) self.address = ft.TextField(
label="Strada si numar",
multiline=True,
min_lines=3,
max_lines=5
)
self.city = ft.TextField(
label="Oras"
)
self.company_name = ft.TextField(label="Denumire firma") self.company_name = ft.TextField(label="Denumire firma")
self.vat = ft.TextField(label="CUI") self.vat = ft.TextField(label="CUI")
self.register_number = ft.TextField(label="Numar registru comert") self.register_number = ft.TextField(label="Numar registru comert")
@@ -27,10 +36,12 @@ class Clients:
self.client_column = ft.Column( self.client_column = ft.Column(
[ [
self.user_name, self.first_name,
self.last_name,
self.email, self.email,
self.phone, self.phone,
self.address, self.address,
self.city,
ft.Button("Persoana Juridica?", on_click=self.on_is_comopany_btn_click), ft.Button("Persoana Juridica?", on_click=self.on_is_comopany_btn_click),
self.company_placeholder, self.company_placeholder,
ft.Button("Adresa de livrare difera de adresa de domiciliu?", on_click=self.on_second_address), ft.Button("Adresa de livrare difera de adresa de domiciliu?", on_click=self.on_second_address),
@@ -130,12 +141,37 @@ class Clients:
self.view_second_address_placeholder.controls.clear() self.view_second_address_placeholder.controls.clear()
def on_edit_btn_click(self, item): def on_edit_btn_click(self, item):
print(item)
self.edit_id = item['id'] self.edit_id = item['id']
self.user_name.value = item['name'] prenume = ''
if '~' in item['name']:
prenume = item['name'].split('~')[0]
self.first_name.value=prenume
nume = ''
if '~' in item['name']:
nume = item['name'].split('~')[1]
self.last_name.value=nume
address = ''
if item['address']:
if "~" in item['address']:
address = item['address'].split("~")[0].split("%")[1]
else:
address = item['address'].split("%")[1]
self.address.value = address
city = ''
if item['address']:
if "~" in item['address']:
city = item['address'].split("~")[0].split("%")[0]
else:
city = item['address'].split("%")[0]
self.city.value = city
self.email.value = item['email'] self.email.value = item['email']
self.email.disabled = True self.email.disabled = True
self.phone.value = item['phone'] self.phone.value = item['phone']
self.address.value = item['address'].split("~")[0]
company = self.company_manager.get_company(item['id']) company = self.company_manager.get_company(item['id'])
if company: if company:
self.company_name.value = company['name'] self.company_name.value = company['name']
@@ -168,10 +204,10 @@ class Clients:
self.page.close(self.confirm_delete_alert) self.page.close(self.confirm_delete_alert)
def on_view_btn_click(self, item): def on_view_btn_click(self, item):
self.view_name.value = f"Nume: {item['name']}" self.view_name.value = f"Nume: {item['name'].replace("~", " ")}"
self.view_email.value = f"E-mail: {item['email']}" self.view_email.value = f"E-mail: {item['email']}"
self.view_phone.value = f"Telefon: {item['phone']}" self.view_phone.value = f"Telefon: {item['phone']}"
self.view_address.value = f"Adresa: {item['address'].split("~")[0]}" self.view_address.value = f"Adresa: {item['address'].split("~")[0].replace("%", ", ")}"
company = self.company_manager.get_company(item['id']) company = self.company_manager.get_company(item['id'])
if company: if company:
self.view_company_placeholder.controls.append(ft.Text("")) self.view_company_placeholder.controls.append(ft.Text(""))
@@ -200,7 +236,7 @@ class Clients:
ft.Column( ft.Column(
[ [
ft.Text(value=item['name'], weight=ft.FontWeight.BOLD), ft.Text(value=item['name'].replace("~", " "), weight=ft.FontWeight.BOLD),
ft.Text(value=item['email'], size=12) ft.Text(value=item['email'], size=12)
] ]
), ),
@@ -236,7 +272,7 @@ class Clients:
] ]
def on_save_btn_click(self, e): def on_save_btn_click(self, e):
user_name = self.user_name.value user_name = self.first_name.value + "~" + self.last_name.value
user_email = self.email.value user_email = self.email.value
user_phone = self.phone.value user_phone = self.phone.value
user_address = self.address.value user_address = self.address.value
@@ -279,8 +315,10 @@ class Clients:
self.clear_fileds() self.clear_fileds()
def clear_fileds(self): def clear_fileds(self):
self.user_name.value = '' self.first_name.value = ''
self.user_name.update() self.first_name.update()
self.last_name.value = ''
self.last_name.update()
self.email.value = '' self.email.value = ''
self.email.update() self.email.update()
self.phone.value = '' self.phone.value = ''

View File

@@ -0,0 +1,80 @@
import flet as ft
import json
class CompanyData:
def __init__(self, page: ft.Page):
self.page = page
self.data =self.load_data()
self.comapny_name = ft.TextField(label = "Denumire firma:", value=self.data['company_name'] if self.data else '')
self.vat = ft.TextField(label="CUI", value=self.data['vat'] if self.data else '')
self.register_number = ft.TextField(label="Numar registru comert", value=self.data['register_number'] if self.data else '')
self.company_address = ft.TextField(label="Sediu", value=self.data['address'] if self.data else '')
self.error_mseeage = ft.Text("")
def on_save_btn_click(self, e):
data = {
'company_name': self.comapny_name.value,
'vat': self.vat.value,
'register_number': self.register_number.value,
'address': self.company_address.value
}
try:
with open('instance/company_data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
self.page.snack_bar = ft.SnackBar(ft.Text('Datele companiei au fost salvate cu succes!'))
self.page.snack_bar.open = True
self.page.update()
self.error_mseeage.value = "Datele au fost salvate cu succes!"
self.error_mseeage.update()
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la salvare: {ex}'))
self.page.snack_bar.open = True
self.page.update()
def load_data(self):
try:
with open('instance/company_data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'company_name': '',
'vat': '',
'register_number': '',
'address': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul company_data.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea datelor companiei: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Despre noi:", size=18, weight=ft.FontWeight.BOLD),
self.comapny_name,
self.vat,
self.register_number,
self.company_address,
ft.Text(),
ft.Row(
[
ft.FilledButton("Salveaza", icon=ft.Icons.SAVE, on_click=self.on_save_btn_click)
],
alignment=ft.MainAxisAlignment.CENTER
)
]
)
)

View File

@@ -0,0 +1,117 @@
import flet as ft
import json
class CompanyDetails:
def __init__(self, page: ft.Page):
self.page = page
self.data = self.load_data()
self.confidentialty_policy = ft.TextField(
multiline=True,
min_lines=5,
max_lines=10,
value=self.data['confidentialty_policy'] if self.data else ''
)
self.delivery_policy = ft.TextField(
multiline=True,
min_lines=5,
max_lines=10,
value=self.data['delivery_policy'] if self.data else ''
)
self.cancel_policy = ft.TextField(
multiline=True,
min_lines=5,
max_lines=10,
value=self.data['cancel_policy'] if self.data else ''
)
self.gdpr = ft.TextField(
multiline=True,
min_lines=5,
max_lines=10,
value=self.data['gdpr'] if self.data else ''
)
self.terms_and_conditions = ft.TextField(
multiline=True,
min_lines=5,
max_lines=10,
value=self.data['terms_and_conditions'] if self.data else ''
)
self.error_mseeage = ft.Text("")
def on_save_btn_click(self, e):
policies = {
'confidentialty_policy': self.confidentialty_policy.value,
'delivery_policy': self.delivery_policy.value,
'cancel_policy': self.cancel_policy.value,
'gdpr': self.gdpr.value,
'terms_and_conditions': self.terms_and_conditions.value
}
try:
with open('instance/policies.json', 'w', encoding='utf-8') as f:
json.dump(policies, f, ensure_ascii=False, indent=4)
self.page.snack_bar = ft.SnackBar(ft.Text('Politicile au fost salvate cu succes!'))
self.page.snack_bar.open = True
self.page.update()
self.error_mseeage.value = "Datele au fost salvate cu succes!"
self.error_mseeage.update()
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la salvare: {ex}'))
self.page.snack_bar.open = True
self.page.update()
def load_data(self):
try:
with open('instance/policies.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'confidentialty_policy': '',
'delivery_policy': '',
'cancel_policy': '',
'gdpr': '',
'terms_and_conditions': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul policies.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea politicilor: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Politica de confidențialitate", size=18, weight=ft.FontWeight.BOLD),
self.confidentialty_policy,
ft.Divider(height=1),
ft.Text("Politica de livrare comandă", size=18, weight=ft.FontWeight.BOLD),
self.delivery_policy,
ft.Divider(height=1),
ft.Text("Politica de anulare comandă", size=18, weight=ft.FontWeight.BOLD),
self.cancel_policy,
ft.Divider(height=1),
ft.Text("Politica GDPR (siguranța datelor cu caracter personal)", size=18, weight=ft.FontWeight.BOLD),
self.gdpr,
ft.Divider(height=1),
ft.Text("Termeni și condiții", size=18, weight=ft.FontWeight.BOLD),
self.terms_and_conditions,
ft.Text(),
self.error_mseeage,
ft.Row(
[
ft.FilledButton("Salveaza", icon = ft.Icons.SAVE, on_click=self.on_save_btn_click)
],
alignment=ft.MainAxisAlignment.CENTER
)
],
scroll=ft.ScrollMode.ADAPTIVE
)
)

View File

@@ -4,6 +4,8 @@ from admin.products import ProductsPage
from admin.banner import Banner from admin.banner import Banner
from admin.orders import OrdersPage from admin.orders import OrdersPage
from admin.clients import Clients from admin.clients import Clients
from admin.fidelity_cards import FidelityCards
from admin.settings import Settings
class Dashboard: class Dashboard:
def __init__(self, page: ft.Page): def __init__(self, page: ft.Page):
@@ -52,6 +54,16 @@ class Dashboard:
selected_icon=ft.Icon(ft.Icons.IMAGE_ROUNDED), selected_icon=ft.Icon(ft.Icons.IMAGE_ROUNDED),
label_content=ft.Text("Banner"), label_content=ft.Text("Banner"),
), ),
ft.NavigationRailDestination(
icon=ft.Icons.CARD_GIFTCARD_OUTLINED,
selected_icon=ft.Icon(ft.Icons.CARD_GIFTCARD),
label_content=ft.Text("Card de\nfidelitate"),
),
ft.NavigationRailDestination(
icon=ft.Icons.SETTINGS_APPLICATIONS_OUTLINED,
selected_icon=ft.Icon(ft.Icons.SETTINGS_APPLICATIONS),
label_content=ft.Text("Setari"),
),
ft.NavigationRailDestination( ft.NavigationRailDestination(
icon=ft.Icons.LOGOUT_OUTLINED, icon=ft.Icons.LOGOUT_OUTLINED,
selected_icon=ft.Icon(ft.Icons.LOGOUT_ROUNDED), selected_icon=ft.Icon(ft.Icons.LOGOUT_ROUNDED),
@@ -87,6 +99,14 @@ class Dashboard:
self.placeholder.content = self.banner.build() self.placeholder.content = self.banner.build()
self.placeholder.update() self.placeholder.update()
case 6: case 6:
self.fidelity_cards = FidelityCards(self.page)
self.placeholder.content = self.fidelity_cards.build()
self.placeholder.update()
case 7:
self.settings = Settings(self.page, self)
self.placeholder.content = self.settings.build()
self.placeholder.update()
case 8:
self.page.client_storage.clear() self.page.client_storage.clear()
self.page.session.clear() self.page.session.clear()
self.page.go('/') self.page.go('/')

View File

@@ -0,0 +1,230 @@
import flet as ft
from dbActions.fidelity_cards import FidelityCards as FC
class FidelityCards:
def __init__(self, page: ft.Page):
self.page = page
self.fidelity_cards_manager = FC()
self.all_cards = self.fidelity_cards_manager.get_all_fidelity_cards()
self.list_of_cars = ft.ListView(
controls=self.create_list(self.all_cards, self.on_delete_btn_click, self.on_edit_btn_click),
expand=True,
spacing=10
)
self.search_bar = ft.TextField(label = "Cauta dupa numar de telefon", on_submit=self.on_search_btn_click, expand=True)
self.search_btn = ft.IconButton(icon=ft.Icons.SEARCH, on_click=self.on_search_btn_click)
self.card_id = ft.TextField(label="Id card")
self.card_type = ft.Dropdown(
label='Reducere (%)',
options=[
ft.dropdown.Option(key='10', text="10%"),
ft.dropdown.Option(key='20', text="20%"),
ft.dropdown.Option(key='30', text="30%"),
],
expand=True
)
self.client_name = ft.TextField(label="Nume client")
self.phone_number = ft.TextField(label="Numar telefon")
self.item_to_be_edit = None
self.add_dialog = ft.AlertDialog(
title=ft.Text(value = "Adauga card de fidelitate" if self.item_to_be_edit == None else "Actualizeaza card de fidelitate"),
content=ft.Column(
[
self.card_id,
self.card_type,
self.client_name,
self.phone_number
],
height=250,
width=400
),
actions = [
ft.Button("Salveaza", on_click=self.on_save_btn_click, icon=ft.Icons.SAVE),
ft.TextButton("Anuleaza", on_click=self.on_cancel_btn_click, icon=ft.Icons.CANCEL)
]
)
self.delete_dialog = ft.AlertDialog(
title=ft.Text("Setergeti cardul?"),
actions = [
ft.Button("Da", on_click=self.on_condifm_delete_btn_click, icon=ft.Icons.DELETE),
ft.TextButton("Nu", on_click=self.on_cancel_delete_btn_click, icon=ft.Icons.CANCEL)
]
)
self.item_to_be_delete = None
def on_condifm_delete_btn_click(self, e):
self.fidelity_cards_manager.remove_fidelity_card(self.item_to_be_delete)
self.page.close(self.delete_dialog)
self.update_list()
self.item_to_be_delete = None
def on_cancel_delete_btn_click(self, e):
self.page.close(self.delete_dialog)
self.item_to_be_delete = None
def on_save_btn_click(self, e):
card_id = self.card_id.value
card_type = self.card_type.value
client_name = self.client_name.value
phone_number = self.phone_number.value
if self.item_to_be_edit == None:
self.fidelity_cards_manager.add_fidelity_card(
card_id,
card_type,
client_name,
phone_number
)
else:
self.fidelity_cards_manager.update_card(
self.item_to_be_edit,
card_id,
card_type,
client_name,
phone_number
)
self.item_to_be_edit = None
#set to defualt
self.card_id.value = ''
self.card_id.update()
self.card_type.value = ''
self.card_type.update()
self.client_name.value = ''
self.client_name.update()
self.phone_number.value = ''
self.phone_number.update()
#close popup
self.page.close(self.add_dialog)
#update_list
self.update_list()
def update_list(self):
self.all_cards = self.fidelity_cards_manager.get_all_fidelity_cards()
self.list_of_cars.controls = self.create_list(
self.all_cards,
self.on_delete_btn_click,
self.on_edit_btn_click
)
self.list_of_cars.update()
def on_cancel_btn_click(self, e):
self.page.close(self.add_dialog)
self.item_to_be_edit = None
def on_add_btn_click(self, e):
self.page.open(self.add_dialog)
def create_list(self, items, on_click_handler2, on_click_handler3):
return [
ft.Container(
content=ft.Row(
[
ft.Column(
[
ft.Text(value=f"Nume: {item['client_name']}", weight=ft.FontWeight.BOLD),
ft.Row(
[
ft.Text(value=f"Id card: {item['card_id']};", size=12),
ft.Text(value=f"Reducere: {item['card_type']}%;", size=12),
ft.Text(value=f"Telefon: {item['phone_number']}", size=12)
],
spacing=10
)
]
),
ft.Row(
[
ft.IconButton(
icon=ft.Icons.EDIT,
on_click=lambda e, id=item: on_click_handler3(id),
),
ft.IconButton(
icon=ft.Icons.DELETE,
on_click=lambda e, id=item['id']: on_click_handler2(id),
icon_color=ft.Colors.RED,
),
]
)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
width=300,
bgcolor=ft.Colors.BROWN_50,
border = ft.border.all(1, ft.Colors.GREY),
padding=10,
border_radius=8,
)
for item in items
]
def on_search_btn_click(self, e):
print(self.search_bar.value)
for card in self.all_cards:
buffer = []
if self.search_bar.value in card['phone_number']:
buffer.append(card)
self.list_of_cars.controls = self.create_list(
buffer,
self.on_delete_btn_click,
self.on_edit_btn_click
)
self.list_of_cars.update()
#set to default
self.search_bar.value = ''
self.search_bar.update()
def on_delete_btn_click(self, id):
self.item_to_be_delete = id
self.page.open(self.delete_dialog)
def on_edit_btn_click(self, item):
self.page.open(self.add_dialog)
self.item_to_be_edit = item['id']
self.card_id.value = item['card_id']
self.card_id.update()
self.card_type.value = item['card_type']
self.card_type.update()
self.client_name.value = item['client_name']
self.client_name.update()
self.phone_number.value = item['phone_number']
self.phone_number.update()
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Card de fidelitate", size=18, weight=ft.FontWeight.BOLD),
ft.Button("Adauga", icon=ft.Icons.ADD, on_click=self.on_add_btn_click),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
self.search_bar,
self.search_btn
]
),
self.list_of_cars
],
alignment=ft.MainAxisAlignment.START,
expand=True
),
expand=True
)

View File

@@ -3,6 +3,7 @@ from dbActions.orders import Orders
from dbActions.users import Users from dbActions.users import Users
from dbActions.products import Products from dbActions.products import Products
from helpers.default_user import DefaultUser from helpers.default_user import DefaultUser
from helpers.emails import send_gmail
class OrdersPage: class OrdersPage:
def __init__(self, page: ft.Page): def __init__(self, page: ft.Page):
@@ -141,7 +142,13 @@ class OrdersPage:
), ),
self.products_column, self.products_column,
ft.Divider(), ft.Divider(),
self.buttons_state ft.Row(
[
self.buttons_state,
ft.Button("Sterge", icon = ft.Icons.DELETE, on_click=self.on_order_delete_btn_click)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
)
], ],
expand=True expand=True
) )
@@ -175,6 +182,37 @@ class OrdersPage:
] ]
) )
self.delete_order_dialog = ft.AlertDialog(
title=ft.Text("Sertgeti?"),
actions=[
ft.FilledButton(
"Da",
on_click=self.on_confirm_delete_order
),
ft.FilledButton(
"Nu",
on_click=self.on_cancel_delete_order_btn_click,
bgcolor=ft.Colors.GREY
)
]
)
def on_order_delete_btn_click(self, e):
self.page.open(self.delete_order_dialog)
def on_confirm_delete_order(self, e):
self.orders.remove_order(self.selected_order['id'])
self.all_orders = self.orders.get_orders()[::-1]
self.oll_orders_list.controls = self.create_list(self.all_orders, self.on_order_click)
self.oll_orders_list.update()
self.selected_order = None
self.order_details_placeholder.content = None
self.order_details_placeholder.update()
self.page.close(self.delete_order_dialog)
def on_cancel_delete_order_btn_click(self, e):
self.page.close(self.delete_order_dialog)
def on_radio_value_change(self, e): def on_radio_value_change(self, e):
self.page.open(self.change_state_dialog) self.page.open(self.change_state_dialog)
@@ -190,12 +228,22 @@ class OrdersPage:
self.oll_orders_list.update() self.oll_orders_list.update()
self.order_details_placeholder.content = None self.order_details_placeholder.content = None
self.order_details_placeholder.update() self.order_details_placeholder.update()
#-------------------------------------------> TBI Send information email to customer
message = self.message_field.value message = self.message_field.value
self.message_field.value = '' self.message_field.value = ''
self.message_field.update() self.message_field.update()
print(message) print(message)
email = self.customer_email email = self.customer_email
status_values = {
"on_hold":"in asteptare",
"new": "noua",
"in_progress":"in lucru",
"completed": "la curier",
}
send_gmail(
to_email=email,
subject=f"Comanda cumneavoastra este: {status_values[status]}",
body=message
)
def on_cancel_state_btn_click(self, e): def on_cancel_state_btn_click(self, e):
self.buttons_state.value = self.original_status self.buttons_state.value = self.original_status
@@ -230,9 +278,10 @@ class OrdersPage:
if products: if products:
for product in products: for product in products:
prod = self.products.get(product['prdouct_id']) prod = self.products.get(product['prdouct_id'])
print(prod)
if prod: if prod:
prod['pices'] = product['quantity']
self.selected_order_products.append(prod) self.selected_order_products.append(prod)
print(prod)
self.name.value = self.selected_user['name'] if '@default.com' not in self.selected_user['email'] else 'Anonim user' self.name.value = self.selected_user['name'] if '@default.com' not in self.selected_user['email'] else 'Anonim user'
self.name.update() self.name.update()
@@ -259,16 +308,28 @@ class OrdersPage:
) )
image = ft.Image( image = ft.Image(
src=product['image'], src=product['image'],
width=200, width=120,
height=200, height=120,
fit=ft.ImageFit.CONTAIN fit=ft.ImageFit.CONTAIN
) )
quantity_label = ft.Text("Cantitate") quantity_label = ft.Text("Cantitate")
quantity = ft.Text(product['quantity']) quantity = ft.Text(product['quantity'])
quantity_row = ft.Row( pices_label = ft.Text("Numar bucati")
pices = ft.Text(product['pices'])
quantity_row = ft.Column(
[ [
quantity_label, ft.Row(
quantity [
quantity_label,
quantity
]
),
ft.Row(
[
pices_label,
pices
]
)
] ]
) )
product_row = ft.Row( product_row = ft.Row(

View File

@@ -14,8 +14,10 @@ class ProductsPage:
self.description = ft.TextField(label="Descriere", multiline=True, min_lines=3, max_lines=5) self.description = ft.TextField(label="Descriere", multiline=True, min_lines=3, max_lines=5)
self.details = ft.TextField(label="Detalii", multiline=True, min_lines=3, max_lines=5) self.details = ft.TextField(label="Detalii", multiline=True, min_lines=3, max_lines=5)
self.price = ft.TextField(label="Pret") self.price = ft.TextField(label="Pret")
self.discount = ft.TextField(label="Reducere (%)") self.discount = ft.TextField(label="Reducere (%)", value='0')
self.quantity = ft.TextField(label="Cantitate") self.quantity = ft.TextField(label="Cantitate")
self.search_filed = ft.TextField(label="Cauta Produs", on_submit=self.on_search_btn_click, expand=True)
self.search_btn = ft.IconButton(icon=ft.Icons.SEARCH, on_click=self.on_search_btn_click)
self.product_image = ft.Image( self.product_image = ft.Image(
width=150, width=150,
height=150, height=150,
@@ -31,6 +33,7 @@ class ProductsPage:
], ],
expand=True expand=True
) )
self.aviability.value = 'in_stock'
self.category = ft.Dropdown( self.category = ft.Dropdown(
label="Categorie", label="Categorie",
@@ -110,6 +113,19 @@ class ProductsPage:
] ]
) )
def on_search_btn_click(self, e):
search = self.search_filed.value
buffer = []
for product in self._all_products:
if search.lower() in product['name'].lower():
buffer.append(product)
self.products_list.controls.clear()
self.search_filed.value = ''
self.search_filed.update()
self._all_products = self.product_manager.get_all()
self.products_list.controls=self.create_list(buffer, self.edit_product, self.delete_product)
self.products_list.update()
def on_file_picker_result(self, e: ft.FilePickerResultEvent): def on_file_picker_result(self, e: ft.FilePickerResultEvent):
if e.files: if e.files:
file = e.files[0] file = e.files[0]
@@ -227,10 +243,21 @@ class ProductsPage:
ft.Container( ft.Container(
content=ft.Row( content=ft.Row(
[ [
ft.Column( ft.Row(
[ [
ft.Text(value=item['name'], weight=ft.FontWeight.BOLD, size=15), ft.Image(
ft.Text(value=f"Pret: {item['price']}", size=12) src=item['image'],
width=50,
height=50,
fit=ft.ImageFit.COVER,
border_radius=5
),
ft.Column(
[
ft.Text(value=item['name'], weight=ft.FontWeight.BOLD, size=15),
ft.Text(value=f"Pret: {item['price']}", size=12)
]
),
] ]
), ),
ft.Row( ft.Row(
@@ -299,6 +326,12 @@ class ProductsPage:
], ],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN alignment=ft.MainAxisAlignment.SPACE_BETWEEN
), ),
ft.Row(
[
self.search_filed,
self.search_btn,
]
),
self.products_list self.products_list
], ],
alignment=ft.MainAxisAlignment.START, alignment=ft.MainAxisAlignment.START,

62
UI_V2/admin/settings.py Normal file
View File

@@ -0,0 +1,62 @@
import flet as ft
from admin.company_data import CompanyData
from admin.company_details import CompanyDetails
class Settings:
def __init__(self, page: ft.Page, dashboard):
self.page = page
self.dashboard = dashboard
def on_add_policy_btn_click(self, e):
policy = CompanyDetails(self.page)
self.dashboard.placeholder.content = policy.build()
self.dashboard.placeholder.update()
def on_add_data_btn_click(self, e):
data = CompanyData(self.page)
self.dashboard.placeholder.content = data.build()
self.dashboard.placeholder.update()
def build(self):
return ft.Container(
content=ft.Column(
[
ft.GridView(
[
ft.Card(
content=ft.Container(
content=ft.Column(
[
ft.Icon(name=ft.Icons.DATASET, size=100),
ft.Text("Adauga datele companiei"),
ft.Button("Adauga", icon=ft.Icons.ADD, on_click=self.on_add_data_btn_click)
],
horizontal_alignment=ft.CrossAxisAlignment.CENTER
),
padding=10
)
),
ft.Card(
content=ft.Container(
content=ft.Column(
[
ft.Icon(name=ft.Icons.POLICY, size=100),
ft.Text("Adauga politicile companiei"),
ft.Button("Adauga", icon=ft.Icons.ADD, on_click=self.on_add_policy_btn_click)
],
horizontal_alignment=ft.CrossAxisAlignment.CENTER
),
padding=10
)
)
],
spacing=10,
runs_count=4,
max_extent=250,
child_aspect_ratio=1.0,
expand=True,
width=1000
)
]
)
)

View File

@@ -0,0 +1,99 @@
import sqlite3
from typing import Optional
class FidelityCards:
def __init__(self, db_path="instance/app_database.db"):
self.db_path = db_path
self._create_fidelity_cards_table()
def _create_fidelity_cards_table(self):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS fidelity_cards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id TEXT,
card_type INTEGER,
client_name TEXT,
phone_number TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
""")
conn.commit()
def add_fidelity_card(self, card_id, card_type, client_name, phone_number):
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO fidelity_cards (card_id, card_type, client_name, phone_number)
VALUES (?, ?, ?, ?)
""", (card_id, card_type, client_name, phone_number,))
conn.commit()
return True
except sqlite3.IntegrityError:
return False
def get_all_fidelity_cards(self):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT * FROM fidelity_cards
""",)
rows = cursor.fetchall()
result = []
if rows:
for row in rows:
buffer = {
"id": row[0],
"card_id": row[1],
"card_type": row[2],
"client_name": row[3],
"phone_number": row[4],
"created_at": row[5]
}
result.append(buffer)
return result
return []
def get_fidelity_card(self, id):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT * FROM fidelity_cards WHERE id = ?
""",(id, ))
row = cursor.fetchone()
result = []
if row:
result = {
"id": row[0],
"card_id": row[1],
"card_type": row[2],
"client_name": row[3],
"phone_number": row[4],
"created_at": row[5]
}
return result
return None
def remove_fidelity_card(self, id):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
DELETE FROM fidelity_cards WHERE id=?;
''', (id,))
conn.commit()
def update_card(self, id, card_id, card_type, client_name, phone_number):
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
UPDATE fidelity_cards
SET card_id = ?, card_type = ?, client_name = ?, phone_number = ?
WHERE id = ?
""", (card_id, card_type, client_name, phone_number, id))
conn.commit()
return True
except Exception:
return False

View File

@@ -0,0 +1,33 @@
import sqlite3
from typing import Optional
class Netopia:
def __init__(self, db_path="instance/app_database.db"):
self.db_path = db_path
self._create_netopia_table()
def _create_netopia_table(self):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS netopia (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id TEXT,
netopia_id TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
""")
conn.commit()
def add_netopia_card(self, order_id, netopia_id):
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO netopia (order_id, netopia_id)
VALUES (?, ? )
""", (order_id, netopia_id))
conn.commit()
return True
except sqlite3.IntegrityError:
return False

View File

@@ -86,6 +86,15 @@ class Orders:
''', (status, id)) ''', (status, id))
conn.commit() conn.commit()
def update_order_user_id(self, user_id, id):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
UPDATE orders SET user_id = ?
WHERE id = ?
''', (user_id, id))
conn.commit()
def get_order_products(self, id): def get_order_products(self, id):
with sqlite3.connect(self.db_path) as conn: with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor() cursor = conn.cursor()
@@ -155,9 +164,23 @@ class Orders:
return [] return []
def remove_product_from_order(self, order_id, product_id): def remove_product_from_order(self, order_id, product_id):
with sqlite3.connect(self.db_path) as conn: with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute(''' cursor.execute('''
DELETE FROM orders_products WHERE orders_id=? and product_id=?; DELETE FROM orders_products WHERE orders_id=? and product_id=?;
''', (order_id, product_id)) ''', (order_id, product_id))
conn.commit() conn.commit()
def remove_order(self, order_id):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
DELETE FROM orders_products WHERE orders_id=?;
''', (order_id,))
conn.commit()
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
DELETE FROM orders WHERE id=?;
''', (order_id,))
conn.commit()

284
UI_V2/helpers/netopia.py Normal file
View File

@@ -0,0 +1,284 @@
"""
NETOPIA Payments helper for Flet/Flask apps
-------------------------------------------
This module wraps the official NETOPIA Python SDK (API v2) and exposes:
• an easy `start_card_payment(...)` helper you can call from your app
• IPN verification and order status helpers
• an optional Flask Blueprint with REST endpoints you can plug into your
existing backend (or the small Flask service that often accompanies Flet apps)
Requirements:
pip install netopia-sdk flask
Environment variables expected (see README at bottom of file too):
NETOPIA_API_KEY API key from admin.netopia-payments.com
NETOPIA_POS_SIGNATURE POS signature
NETOPIA_PUBLIC_KEY RSA public key (PEM string)
NETOPIA_PRIVATE_KEY RSA private key (PEM string, optional)
NETOPIA_REDIRECT_URL your site URL where the buyer returns after payment
NETOPIA_NOTIFY_URL public URL that receives IPN callbacks
NETOPIA_CANCEL_URL optional cancel URL
NETOPIA_POS_SIGNATURE_SET optional, CSV list of allowed POS signatures
NETOPIA_IS_LIVE 'true' to use production; otherwise sandbox
Usage from Flet (example):
from helpers.netopia import start_card_payment
url_payload = start_card_payment(
order_id="TG-100045",
amount=159.90,
currency="RON",
description="Comandă #TG-100045",
customer={
"email": "client@example.com",
"phone": "0712345678",
"first_name": "Ion",
"last_name": "Popescu",
"city": "București",
"country": "642", # 642 = Romania
"address": "Str. Exemplu 10",
"zip": "010101",
"county": "B-IF",
"language": "ro",
},
products=[
{"name": "Mix nuci 500g", "code": "MX500", "category": "fructe-uscate", "price": 79.95, "vat": 0},
{"name": "Caju 500g", "code": "CJ500", "category": "fructe-uscate", "price": 79.95, "vat": 0},
],
installments=1,
)
# url_payload typically contains the redirect URL open it in a webview or browser
Notes:
• You must expose your IPN endpoint publicly (HTTPS) and configure it in Netopia admin.
• Always trust order status updates coming from IPN, not only the browser redirect.
"""
from __future__ import annotations
import os
from datetime import datetime, timezone
from dataclasses import dataclass
from typing import List, Optional, Dict, Any
# NETOPIA SDK imports
from netopia_sdk.config import Config
from netopia_sdk.client import PaymentClient
from netopia_sdk.payment import PaymentService
from netopia_sdk.requests.models import (
StartPaymentRequest,
ConfigData,
PaymentData,
PaymentOptions,
Instrument,
OrderData,
BillingData,
ProductsData,
ShippingData,
)
# ---------------------------
# Configuration & wiring
# ---------------------------
@dataclass
class NetopiaSettings:
api_key: str
pos_signature: str
public_key_str: str
notify_url: str
redirect_url: str
is_live: bool
pos_signature_set: List[str]
cancel_url: str | None = None
private_key_str: str | None = None
@classmethod
def from_env(cls) -> "NetopiaSettings":
print("API_KEY? ", os.getenv("NETOPIA_API_KEY") is not None)
is_live_str = os.getenv("NETOPIA_IS_LIVE", "false").strip().lower()
is_live = is_live_str in ("1", "true", "yes", "on")
pos_sig = os.environ.get("NETOPIA_POS_SIGNATURE", "").strip()
pos_sig_set_env = os.getenv("NETOPIA_POS_SIGNATURE_SET", pos_sig)
pos_sig_set = [s.strip() for s in pos_sig_set_env.split(",") if s.strip()]
return cls(
api_key=os.environ.get("NETOPIA_API_KEY", "").strip(),
pos_signature=pos_sig,
public_key_str=os.environ.get("NETOPIA_PUBLIC_KEY", "").strip(),
private_key_str=os.environ.get("NETOPIA_PRIVATE_KEY", "").strip(),
notify_url=os.environ.get("NETOPIA_NOTIFY_URL", "").strip(),
redirect_url=os.environ.get("NETOPIA_REDIRECT_URL", "").strip(),
is_live=is_live,
pos_signature_set=pos_sig_set,
cancel_url=os.environ.get("NETOPIA_CANCEL_URL", None),
)
def _build_payment_service(settings: Optional[NetopiaSettings] = None) -> PaymentService:
"""Create a PaymentService from settings/env."""
settings = settings or NetopiaSettings.from_env()
if not settings.api_key:
raise RuntimeError("NETOPIA_API_KEY is missing")
if not settings.pos_signature:
raise RuntimeError("NETOPIA_POS_SIGNATURE is missing")
if not settings.public_key_str:
raise RuntimeError("NETOPIA_PUBLIC_KEY is missing (PEM)")
if not settings.notify_url:
raise RuntimeError("NETOPIA_NOTIFY_URL is missing")
if not settings.redirect_url:
raise RuntimeError("NETOPIA_REDIRECT_URL is missing")
config = Config(
api_key=settings.api_key,
pos_signature=settings.pos_signature,
is_live=settings.is_live,
notify_url=settings.notify_url,
redirect_url=settings.redirect_url,
public_key_str=settings.public_key_str,
#private_key_str=settings.private_key_str,
pos_signature_set=settings.pos_signature_set,
)
client = PaymentClient(config)
return PaymentService(client)
# ---------------------------
# Helpers: BillingData from minimal fields (SDK-supported)
# ---------------------------
def _derive_billing_from_customer(customer: Dict[str, str]) -> BillingData:
"""Construct BillingData using only fields supported by the SDK example.
Accepts a single free-form address but does not send it (SDK BillingData
in v2 typically supports: email, phone, firstName, lastName, city, country).
We try to infer city from the first token before a comma if city is missing.
"""
city = (customer.get("city") or "").strip()
if not city:
addr = (customer.get("address") or "").strip()
if "," in addr:
candidate = addr.split(",", 1)[0].strip()
if 1 <= len(candidate) <= 64:
city = candidate
return BillingData(
email=customer.get("email", ""),
phone=customer.get("phone", ""),
firstName=customer.get("first_name", ""),
lastName=customer.get("last_name", ""),
city=city,
country=int(customer.get("country", "642") or 642),
countryName=customer.get("countryName", "Romania"),
state=customer.get("state", customer.get("county", "")),
postalCode=customer.get("zip", ""),
details=(customer.get("address") or "").strip(),
)
def _derive_shipping_from_customer(customer: Dict[str, str]) -> ShippingData:
city = (customer.get("ship_city") or customer.get("city") or "").strip()
if not city:
addr = (customer.get("ship_address") or customer.get("address") or "").strip()
if "," in addr:
cand = addr.split(",", 1)[0].strip()
if 1 <= len(cand) <= 64:
city = cand
return ShippingData(
email=customer.get("ship_email", customer.get("email", "")),
phone=customer.get("ship_phone", customer.get("phone", "")),
firstName=customer.get("ship_first_name", customer.get("first_name", "")),
lastName=customer.get("ship_last_name", customer.get("last_name", "")),
city=city,
country=int(customer.get("ship_country", customer.get("country", "642")) or 642),
countryName=customer.get("ship_countryName", customer.get("countryName", "Romania")),
state=customer.get("ship_state", customer.get("state", customer.get("county", ""))),
postalCode=customer.get("ship_zip", customer.get("zip", "")),
details=(customer.get("ship_address") or customer.get("address") or "").strip(),
)
# ---------------------------
# Highlevel helpers (call these from your app)
# ---------------------------
def start_card_payment(
*,
order_id: str,
amount: float,
currency: str,
description: str,
customer: Dict[str, str],
products: List[Dict[str, Any]],
installments: int = 1,
settings: Optional[NetopiaSettings] = None,
) -> Dict[str, Any]:
"""Create a redirectbased card payment and return the SDK response.
The response typically includes the URL you must redirect the buyer to.
You should persist the order locally before calling this.
"""
svc = _build_payment_service(settings)
billing = _derive_billing_from_customer(customer)
shipping = _derive_shipping_from_customer(customer)
prods: List[ProductsData] = []
for p in products:
prods.append(
ProductsData(
name=str(p["name"]),
code=str(p.get("code", p["name"]))[:32],
category=str(p.get("category", "")),
price=float(p.get("price", 0.0)),
vat=int(p.get("vat", 0)),
)
)
cfg = NetopiaSettings.from_env() if settings is None else settings
now_iso = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
req = StartPaymentRequest(
config=ConfigData(
emailTemplate=customer.get("email_template", "default"),
emailSubject=customer.get("email_subject", "Order Confirmation"),
cancelUrl=cfg.cancel_url or cfg.redirect_url,
notifyUrl=cfg.notify_url,
redirectUrl=cfg.redirect_url,
language=customer.get("language", "ro"),
),
payment=PaymentData(
options=PaymentOptions(installments=int(installments), bonus=0),
instrument=None,
data={},
),
order=OrderData(
ntpID=None,
posSignature=None,
dateTime=now_iso,
orderID=str(order_id),
amount=float(amount),
currency=str(currency),
description=str(description),
billing=billing,
shipping=shipping,
products=prods,
installments={"selected": int(installments) if installments else 0, "available": []},
data={},
),
)
return svc.start_payment(req)
def verify_ipn(raw_body: bytes, settings: Optional[NetopiaSettings] = None) -> Dict[str, Any]:
"""Verify an IPN payload coming from NETOPIA. Returns the decoded data.
Raise an exception if verification fails.
"""
svc = _build_payment_service(settings)
return svc.verify_ipn(raw_body)
def get_status(*, ntp_id: Optional[str] = None, order_id: Optional[str] = None, settings: Optional[NetopiaSettings] = None) -> Dict[str, Any]:
"""Query order status by ntpID and/or orderID."""
svc = _build_payment_service(settings)
return svc.get_status(ntpID=ntp_id, orderID=order_id)

View File

@@ -7,10 +7,20 @@ from pages.products.product import ProductPage
from pages.profile.profilepage import ProfilePage from pages.profile.profilepage import ProfilePage
from pages.shopping_cart.cart import Cart from pages.shopping_cart.cart import Cart
from pages.shopping_cart.peload_card import PreloadCard from pages.shopping_cart.peload_card import PreloadCard
from pages.shopping_cart.payment_redirect import PaymentRedirect
from pages.details.about_us import AboutUS
from pages.details.terms_and_conditions import TermsAndConditions
from pages.details.cancel_policy import CancelPolicy
from pages.details.confidentialty_policy import ConfidentialtyPolicy
from pages.details.delivery_policy import DeliveryPolicys
from pages.details.gdpr_policy import GDPR
import os import os
os.environ["FLET_SECRET_KEY"] = os.urandom(12).hex() os.environ["FLET_SECRET_KEY"] = os.urandom(12).hex()
from dotenv import load_dotenv
load_dotenv()
def main(page: ft.Page): def main(page: ft.Page):
page.title = "Taina Gustului" page.title = "Taina Gustului"
page.theme_mode = ft.ThemeMode.LIGHT page.theme_mode = ft.ThemeMode.LIGHT
@@ -36,8 +46,8 @@ def main(page: ft.Page):
if route == "/admin": if route == "/admin":
if not page.client_storage.get("is_authenticated"): if not page.client_storage.get("is_authenticated"):
page.go("/auth") page.go("/auth")
return return
dashbaord = Dashboard(page) dashbaord = Dashboard(page)
page.add(dashbaord.build()) page.add(dashbaord.build())
page.update() page.update()
@@ -73,6 +83,48 @@ def main(page: ft.Page):
page.update() page.update()
return return
if route == '/payment/redirect':
redirect = PaymentRedirect(page)
page.add(redirect.build())
page.update()
return
if route == '/about_us':
about_us = AboutUS(page)
page.add(about_us.build())
page.update()
return
if route == '/termeni_si_conditii':
termeni_si_conditii = TermsAndConditions(page)
page.add(termeni_si_conditii.build())
page.update()
return
if route == '/politica_de_anulare_comanda':
politica_de_anulare_comanda = CancelPolicy(page)
page.add(politica_de_anulare_comanda.build())
page.update()
return
if route == '/politica_de_confidentialitate':
politica_de_confidentialitate = ConfidentialtyPolicy(page)
page.add(politica_de_confidentialitate.build())
page.update()
return
if route == '/politica_de_livrare_comanda':
politica_de_livrare_comanda = DeliveryPolicys(page)
page.add(politica_de_livrare_comanda.build())
page.update()
return
if route == '/gdpr':
gdpr = GDPR(page)
page.add(gdpr.build())
page.update()
return
# 5) Fallback 404 # 5) Fallback 404
page.add(ft.Text("404: Page not found")) page.add(ft.Text("404: Page not found"))
page.update() page.update()

View File

@@ -14,25 +14,28 @@ class Login:
self.register = Register(self.page, self.auth, self) self.register = Register(self.page, self.auth, self)
def on_login_btn_click(self, e): def on_login_btn_click(self, e):
email = self.email.value try:
password = self.password.value email = self.email.value
password_hash = self.user_manager.hash_password(password) password = self.password.value
user = self.user_manager.authenticate_user(email, password_hash) password_hash = self.user_manager.hash_password(password)
if user: user = self.user_manager.authenticate_user(email, password_hash)
self.page.client_storage.set("is_authenticated", True) if user:
self.page.session.set("user", user) self.page.client_storage.set("is_authenticated", True)
self.error_message.value = '' self.page.session.set("user", user)
self.error_message.update() self.error_message.value = ''
if user['role'] == 'admin': self.error_message.update()
self.page.go('/admin') if user['role'] == 'admin':
else: self.page.go('/admin')
if user['name'] is None or len(user['name'])<=1:
self.page.go("/profil")
else: else:
self.page.go('/') if user['name'] is None or len(user['name'])<=1:
else: self.page.go("/profil")
self.error_message.value = 'E-mail sau parola sunt gresite!' else:
self.error_message.update() self.page.go('/')
else:
self.error_message.value = 'E-mail sau parola sunt gresite!'
self.error_message.update()
except Exception as e:
print("Unable to login, error: ", e)
def on_register_btn_click(self, e): def on_register_btn_click(self, e):
self.auth.placeholder.content = self.register.build() self.auth.placeholder.content = self.register.build()

View File

@@ -92,12 +92,15 @@ class Register:
if self._check_repeat_password(password, repeat_password): if self._check_repeat_password(password, repeat_password):
print("Password is valid!") print("Password is valid!")
password_hash = self.users_manager.hash_password(password) password_hash = self.users_manager.hash_password(password)
self.users_manager.register_user(email, password_hash) if self.users_manager.register_user(email, password_hash):
self.error_message.value = "Inregistrarea a avut loc cu succes, va puteti autentifica!" self.error_message.value = "Inregistrarea a avut loc cu succes, va puteti autentifica!"
self.error_message.color = ft.Colors.GREEN self.error_message.color = ft.Colors.GREEN
self.error_message.update() self.error_message.update()
time.sleep(3) time.sleep(3)
self.on_login_btn_click('') self.on_login_btn_click('')
else:
self.error_message.value = 'Exita deja un cont cu acet email. Daca ati fost invitat sau ati uitat prola accesati rubrica "Ai uitat parola?"'
self.error_message.update()
def build(self): def build(self):
return ft.Column( return ft.Column(

View File

@@ -94,6 +94,8 @@ class Category:
self.products_group.controls.clear() self.products_group.controls.clear()
self.add_products(buffer) self.add_products(buffer)
self.products_group.update() self.products_group.update()
self.searchbar.value = ''
self.searchbar.update()
def on_profile_btn_click(self, e): def on_profile_btn_click(self, e):
self.page.go('/profil') self.page.go('/profil')

View File

@@ -0,0 +1,55 @@
import flet as ft
import json
class AboutUS:
def __init__(self, page:ft.Page):
self.page = page
self.data = self.load_data()
self.company_name = ft.Text(value=self.data['company_name'])
self.vat = ft.Text(value=self.data['vat'])
self.register_number = ft.Text(value=self.data['register_number'])
self.address = ft.Text(value=self.data['address'])
def load_data(self):
try:
with open('instance/company_data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'company_name': '',
'vat': '',
'register_number': '',
'address': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul company_data.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea datelor companiei: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Row(
[
ft.Image(src="images/tainagustului.png", width=100),
ft.Column(
[
ft.Text("Despre noi", size=18, weight=ft.FontWeight.BOLD),
self.company_name,
self.vat,
self.register_number,
self.address
]
)
],
alignment=ft.MainAxisAlignment.CENTER,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
scroll=ft.ScrollMode.ADAPTIVE
)
)

View File

@@ -0,0 +1,44 @@
import flet as ft
import json
class CancelPolicy:
def __init__(self, page: ft.Page):
self.page = page
def load_data(self):
try:
with open('instance/policies.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'confidentialty_policy': '',
'delivery_policy': '',
'cancel_policy': '',
'gdpr': '',
'terms_and_conditions': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul policies.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea politicilor: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Politica de anulare comandă",size = 18, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
ft.Text(value=self.load_data()['cancel_policy'])
],
alignment=ft.MainAxisAlignment.START,
scroll=ft.ScrollMode.ADAPTIVE
),
padding=10,
expand=True
)

View File

@@ -0,0 +1,44 @@
import flet as ft
import json
class ConfidentialtyPolicy:
def __init__(self, page: ft.Page):
self.page = page
def load_data(self):
try:
with open('instance/policies.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'confidentialty_policy': '',
'delivery_policy': '',
'cancel_policy': '',
'gdpr': '',
'terms_and_conditions': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul policies.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea politicilor: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Politica de confidențialitate",size = 18, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
ft.Text(value=self.load_data()['confidentialty_policy'])
],
alignment=ft.MainAxisAlignment.START,
scroll=ft.ScrollMode.ADAPTIVE
),
padding=10,
expand=True
)

View File

@@ -0,0 +1,44 @@
import flet as ft
import json
class DeliveryPolicys:
def __init__(self, page: ft.Page):
self.page = page
def load_data(self):
try:
with open('instance/policies.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'confidentialty_policy': '',
'delivery_policy': '',
'cancel_policy': '',
'gdpr': '',
'terms_and_conditions': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul policies.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea politicilor: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Politica de livrare comandă",size = 18, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
ft.Text(value=self.load_data()['delivery_policy'])
],
alignment=ft.MainAxisAlignment.START,
scroll=ft.ScrollMode.ADAPTIVE
),
padding=10,
expand=True
)

View File

@@ -0,0 +1,44 @@
import flet as ft
import json
class GDPR:
def __init__(self, page: ft.Page):
self.page = page
def load_data(self):
try:
with open('instance/policies.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'confidentialty_policy': '',
'delivery_policy': '',
'cancel_policy': '',
'gdpr': '',
'terms_and_conditions': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul policies.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea politicilor: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Politica GDPR (siguranța datelor cu caracter personal)",size = 18, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
ft.Text(value=self.load_data()['gdpr'])
],
alignment=ft.MainAxisAlignment.START,
scroll=ft.ScrollMode.ADAPTIVE
),
padding=10,
expand=True
)

View File

@@ -0,0 +1,44 @@
import flet as ft
import json
class TermsAndConditions:
def __init__(self, page: ft.Page):
self.page = page
def load_data(self):
try:
with open('instance/policies.json', 'r', encoding='utf-8') as f:
data = json.load(f)
return data
except FileNotFoundError:
return {
'confidentialty_policy': '',
'delivery_policy': '',
'cancel_policy': '',
'gdpr': '',
'terms_and_conditions': ''
}
except json.JSONDecodeError:
self.page.snack_bar = ft.SnackBar(ft.Text('Eroare: fișierul policies.json este corupt.'))
self.page.snack_bar.open = True
self.page.update()
return None
except Exception as ex:
self.page.snack_bar = ft.SnackBar(ft.Text(f'Eroare la citirea politicilor: {ex}'))
self.page.snack_bar.open = True
self.page.update()
return None
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("Termeni si conditii",size = 18, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
ft.Text(value=self.load_data()['terms_and_conditions'])
],
alignment=ft.MainAxisAlignment.START,
scroll=ft.ScrollMode.ADAPTIVE
),
padding=10,
expand=True
)

View File

@@ -26,7 +26,7 @@ class Home:
expand=True, expand=True,
width=1000 width=1000
) )
self.products = self.products_manager.get_all() self.products = self.products_manager.get_all()[:20]
self.add_products(self.products) self.add_products(self.products)
self.searchbar = ft.TextField( self.searchbar = ft.TextField(
@@ -64,6 +64,7 @@ class Home:
self.products_group.controls.clear() self.products_group.controls.clear()
self.add_products(buffer) self.add_products(buffer)
self.page.session.set("search_for", None) self.page.session.set("search_for", None)
self.searchbar.value = ''
def on_acasa_btn_click(self, e): def on_acasa_btn_click(self, e):
self.page.go('/') self.page.go('/')
@@ -158,12 +159,32 @@ class Home:
def on_search_btn_click(self, e): def on_search_btn_click(self, e):
search = self.searchbar.value search = self.searchbar.value
buffer = [] buffer = []
for product in self.products: for product in self.products_manager.get_all():
if search.lower() in product['name'].lower(): if search.lower() in product['name'].lower():
buffer.append(product) buffer.append(product)
self.products_group.controls.clear() self.products_group.controls.clear()
self.add_products(buffer) self.add_products(buffer)
self.products_group.update() self.products_group.update()
self.searchbar.value = ''
self.searchbar.update()
def on_about_us_btn_click(self, e):
self.page.go('/about_us')
def on_terms_and_cond_btn_click(self, e):
self.page.go("/termeni_si_conditii")
def on_cancel_policy_btn_click(self, e):
self.page.go("/politica_de_anulare_comanda")
def on_confidentiality_policy_btn_click(self, e):
self.page.go('/politica_de_confidentialitate')
def on_delivery_policy_btn_click(self, e):
self.page.go("/politica_de_livrare_comanda")
def on_gdpr_btn_click(self, e):
self.page.go("/gdpr")
def build(self): def build(self):
return ft.Container( return ft.Container(
@@ -201,10 +222,33 @@ class Home:
alignment=ft.MainAxisAlignment.CENTER, alignment=ft.MainAxisAlignment.CENTER,
width=1000 width=1000
), ),
self.products_group self.products_group,
ft.Divider(height=1),
ft.Row(
[
ft.Column(
[
ft.TextButton("Despre noi", on_click=self.on_about_us_btn_click, icon=ft.Icons.INFO),
ft.TextButton("Termeni si conditii", on_click=self.on_terms_and_cond_btn_click, icon=ft.Icons.INFO),
ft.TextButton("Politica de anulare comanda",on_click=self.on_cancel_policy_btn_click, icon=ft.Icons.INFO),
ft.TextButton("Politica de confidentialitate",on_click=self.on_confidentiality_policy_btn_click, icon=ft.Icons.INFO),
ft.TextButton("Politica de livrare comanda",on_click=self.on_delivery_policy_btn_click, icon=ft.Icons.INFO),
ft.TextButton("Politica GDPR (siguranța datelor cu caracter personal)",on_click=self.on_gdpr_btn_click, icon=ft.Icons.INFO)
]
),
ft.Column(
[
ft.TextButton("TainaGustului", icon=ft.Icons.FACEBOOK)
]
)
],
alignment=ft.MainAxisAlignment.SPACE_AROUND,
vertical_alignment=ft.CrossAxisAlignment.START
)
], ],
horizontal_alignment=ft.CrossAxisAlignment.CENTER, horizontal_alignment=ft.CrossAxisAlignment.CENTER,
expand=True expand=True,
width=1000
) )
], ],
scroll=ft.ScrollMode.ADAPTIVE, scroll=ft.ScrollMode.ADAPTIVE,

View File

@@ -74,7 +74,7 @@ class ProductPage:
ft.Stack( ft.Stack(
[ [
ft.Image( ft.Image(
src=f"images/{sp['image']}", src=f"{sp['image']}",
fit=ft.ImageFit.COVER, fit=ft.ImageFit.COVER,
repeat=ft.ImageRepeat.NO_REPEAT, repeat=ft.ImageRepeat.NO_REPEAT,
border_radius=ft.border_radius.all(5), border_radius=ft.border_radius.all(5),
@@ -104,7 +104,7 @@ class ProductPage:
], ],
alignment=ft.MainAxisAlignment.CENTER alignment=ft.MainAxisAlignment.CENTER
), ),
ft.IconButton(ft.Icons.ADD_SHOPPING_CART, on_click=lambda e, i=sp: self.on_sp_add_to_cart_click(i)) #ft.IconButton(ft.Icons.ADD_SHOPPING_CART, on_click=lambda e, i=sp: self.on_sp_add_to_cart_click(i))
], ],
width=200, width=200,
alignment=ft.MainAxisAlignment.SPACE_BETWEEN alignment=ft.MainAxisAlignment.SPACE_BETWEEN
@@ -358,7 +358,9 @@ class ProductPage:
self.page.go('/') self.page.go('/')
def on_sp_product_click(self, product): def on_sp_product_click(self, product):
self.page.session.set('product', product) self.page.session.set("product", product)
name = product['name'].replace(" ", "-").lower()
self.page.go(f'/produs/{name}')
def ask_for_create_user(self): def ask_for_create_user(self):
self.page.open(self.ask_for_login_dialog) self.page.open(self.ask_for_login_dialog)

View File

@@ -9,15 +9,46 @@ class ProfilePage:
self.company_manager = Company() self.company_manager = Company()
self.user = self.page.session.get("user") self.user = self.page.session.get("user")
self.company = self.company_manager.get_company(self.user['id']) self.company = self.company_manager.get_company(self.user['id'])
self.user_name = ft.TextField(label="Nume si Prenume", value=self.user['name']) prenume = ''
if "@default.com" not in self.user['email']:
if '~' in self.user['name']:
prenume = self.user['name'].split('~')[0]
self.first_name = ft.TextField(
label="Prenume",
value=prenume
)
nume = ''
if "@default.com" not in self.user['email']:
if '~' in self.user['name']:
nume = self.user['name'].split('~')[1]
self.last_name = ft.TextField(
label="Nume",
value=nume
)
self.email = ft.TextField(label="E-mail", value=self.user['email'], read_only=True) self.email = ft.TextField(label="E-mail", value=self.user['email'], read_only=True)
self.phone = ft.TextField(label="Telefon", value=self.user['phone']) self.phone = ft.TextField(label="Telefon", value=self.user['phone'])
address = ''
if self.user['address']:
if "~" in self.user['address']:
address = self.user['address'].split("~")[0].split("%")[1]
else:
address = self.user['address'].split("%")[1]
self.address = ft.TextField( self.address = ft.TextField(
label="Adresa", label="Strada si numar",
multiline=True, multiline=True,
min_lines=3, min_lines=3,
max_lines=5, max_lines=5,
value = self.user['address'].split("~")[0] if self.user['address'] else '' value = address
)
city = ''
if self.user['address']:
if "~" in self.user['address']:
city = self.user['address'].split("~")[0].split("%")[0]
else:
city = self.user['address'].split("%")[0]
self.city = ft.TextField(
label="Oras",
value = city
) )
self.company_name = ft.TextField( self.company_name = ft.TextField(
label="Denumire firma", label="Denumire firma",
@@ -39,11 +70,11 @@ class ProfilePage:
value=self.company['address'] if self.company else '') value=self.company['address'] if self.company else '')
self.second_address_placeholder = ft.Column() self.second_address_placeholder = ft.Column()
self.second_address = ft.TextField( self.second_address = ft.TextField(
label="Adresa de livrare", label="Adresa de livrare (str, nr, oras, judet)",
multiline=True, multiline=True,
min_lines=3, min_lines=3,
max_lines=5, max_lines=5,
value=self.user['address'].split("~")[1] if self.user['address'] and len(self.user['address'].split("~"))>1 else '' value=self.user['address'].split("~")[1] if self.user['address'] and "~" in self.user['address'] and len(self.user['address'].split("~"))>1 else ''
) )
self.second_address_placeholder =ft.Column() self.second_address_placeholder =ft.Column()
self.order_placeholder =ft.Column() self.order_placeholder =ft.Column()
@@ -75,7 +106,7 @@ class ProfilePage:
self.order_placeholder.controls.append(self.company_address) self.order_placeholder.controls.append(self.company_address)
self.order_placeholder.update() self.order_placeholder.update()
def check_inserted_user_data(self, username, phone, address): def check_inserted_user_data(self, username, phone, address, city):
found = False found = False
if username is None or len(username)< 1: if username is None or len(username)< 1:
found = True found = True
@@ -83,6 +114,8 @@ class ProfilePage:
found = True found = True
if address is None or len(address)< 1: if address is None or len(address)< 1:
found = True found = True
if city is None or len(city)< 1:
found = True
if found: if found:
self.error_message.value = "Toate campurile sunt obligatori!" self.error_message.value = "Toate campurile sunt obligatori!"
self.error_message.color = ft.Colors.RED self.error_message.color = ft.Colors.RED
@@ -115,14 +148,14 @@ class ProfilePage:
return found return found
def on_save_btn_click(self, e): def on_save_btn_click(self, e):
username = self.user_name.value username = self.first_name.value + "~" + self.last_name.value
phone = self.phone.value phone = self.phone.value
address = self.address.value address = self.city.value+"%"+self.address.value
if self.is_second_address: if self.is_second_address:
if self.check_second_address_inserted(self.second_address.value): if self.check_second_address_inserted(self.second_address.value):
return return
address = self.address.value + '~' + self.second_address.value address = self.city.value+"%"+self.address.value + '~' + self.second_address.value
if self.check_inserted_user_data(username, phone, address): if self.check_inserted_user_data(username, phone, self.address.value, self.city.value):
return return
self.user_manager.update_user_data(username, phone, address, self.user['id']) self.user_manager.update_user_data(username, phone, address, self.user['id'])
@@ -148,6 +181,35 @@ class ProfilePage:
self.error_message.value = "Profilul a fost salvat!" self.error_message.value = "Profilul a fost salvat!"
self.error_message.color = ft.Colors.GREEN self.error_message.color = ft.Colors.GREEN
self.error_message.update() self.error_message.update()
self.user = self.user_manager.get(self.user['id'])
self.page.session.set('user',self.user)
self.first_name.value=self.user['name'].split('~')[0]
self.first_name.update()
self.last_name.value=self.user['name'].split('~')[1]
self.last_name.update()
self.phone.value = self.user['phone']
self.phone.update()
self.address.value = self.user['address'].split("~")[0].split("%")[1] if self.user['address'] else ''
self.address.update()
self.city.value = self.user['address'].split("~")[0].split("%")[0] if self.user['address'] else ''
self.city.update()
try:
self.company = self.company_manager.get_company(self.user['id'])
self.company_name.value=self.company['name'] if self.company else ''
self.company_name.update()
self.vat.value = self.company['vat'] if self.company else ''
self.vat.update()
self.register_number.value=self.company['register_number'] if self.company else ''
self.register_number.update()
self.company_address.value=self.company['address'] if self.company else ''
self.company_address.update()
except Exception as e:
print ("Unable to save company data", e)
try:
self.second_address.value=self.user['address'].split("~")[1] if self.user['address'] and len(self.user['address'].split("~"))>1 else ''
self.second_address.update()
except Exception as e:
print("Unable to save delivery address.", e)
def on_back_btn_click(self, e): def on_back_btn_click(self, e):
self.page.go('/') self.page.go('/')
@@ -171,10 +233,12 @@ class ProfilePage:
alignment=ft.MainAxisAlignment.END alignment=ft.MainAxisAlignment.END
), ),
ft.Icon(name=ft.Icons.ACCOUNT_CIRCLE, size=100), ft.Icon(name=ft.Icons.ACCOUNT_CIRCLE, size=100),
self.user_name, self.first_name,
self.last_name,
self.email, self.email,
self.phone, self.phone,
self.address, self.address,
self.city,
ft.Divider(), ft.Divider(),
ft.Text("Adresa de livrare difera de adresa de domiciliu?", text_align=ft.TextAlign.CENTER), ft.Text("Adresa de livrare difera de adresa de domiciliu?", text_align=ft.TextAlign.CENTER),
ft.Button("Adauga adresa livrare", width=400, on_click=self.on_second_address_btn_click), ft.Button("Adauga adresa livrare", width=400, on_click=self.on_second_address_btn_click),

View File

@@ -3,19 +3,26 @@ from dbActions.orders import Orders
from dbActions.products import Products from dbActions.products import Products
from dbActions.company import Company from dbActions.company import Company
from dbActions.users import Users from dbActions.users import Users
from dbActions.fidelity_cards import FidelityCards
from dbActions.netopia import Netopia
from helpers.emails import send_gmail
from helpers.netopia import start_card_payment
import re
class Cart: class Cart:
def __init__(self, page: ft.Page): def __init__(self, page: ft.Page):
self.page = page self.page = page
self.user = self.page.session.get('user') self.user = self.page.session.get('user')
self.orders = Orders() self.orders_manager = Orders()
self.productsDB = Products() self.productsDB = Products()
self.company_manager = Company() self.company_manager = Company()
self.user_manager = Users() self.user_manager = Users()
self.card_manager = FidelityCards()
self.netopia_manager = Netopia()
self.products = [] self.products = []
self.is_second_address = None self.is_second_address = None
self.is_company = None self.is_company = None
self.pret_total = ft.Text("Pret total: 0", weight=ft.FontWeight.BOLD)
self.delete_dialog = ft.AlertDialog( self.delete_dialog = ft.AlertDialog(
title=ft.Text("Stergeti?"), title=ft.Text("Stergeti?"),
actions=[ actions=[
@@ -24,9 +31,9 @@ class Cart:
] ]
) )
self.on_hold_orders = self.orders.get_on_hold_order(self.user['id']) self.on_hold_orders = self.orders_manager.get_on_hold_order(self.user['id'])
if self.on_hold_orders: if self.on_hold_orders:
self.order_products = self.orders.get_order_products(self.on_hold_orders['id']) self.order_products = self.orders_manager.get_order_products(self.on_hold_orders['id'])
for product in self.order_products: for product in self.order_products:
self.products.append( self.products.append(
@@ -70,36 +77,65 @@ class Cart:
] ]
) )
if '@default.com' not in self.user['email']: self.all_orders = self.orders_manager.get_orders_for_user(self.page.session.get('user')['id'])
self.all_orders = self.orders.get_orders_for_user(self.page.session.get('user')['id']) self.all_orders = self.all_orders[::-1]
self.all_orders = self.all_orders[::-1] self.orders_manager_list = ft.ListView(
self.orders_list = ft.ListView( controls=self.create_history_list(self.all_orders),
controls=self.create_history_list(self.all_orders), spacing=10,
spacing=10, expand=True
expand=True )
)
self.user = self.page.session.get("user") self.user = self.page.session.get("user")
self.company = self.company_manager.get_company(self.user['id']) self.company = self.company_manager.get_company(self.user['id'])
self.user_name = ft.TextField( prenume = ''
label="Nume si Prenume", if "@default.com" not in self.user['email']:
value=self.user['name'] if "@default.com" not in self.user['email'] else None if '~' in self.user['name']:
prenume = self.user['name'].split('~')[0]
self.first_name = ft.TextField(
label="Prenume",
value=prenume
) )
nume = ''
if "@default.com" not in self.user['email']:
if '~' in self.user['name']:
nume = self.user['name'].split('~')[1]
self.last_name = ft.TextField(
label="Nume",
value=nume
)
self.email = ft.TextField( self.email = ft.TextField(
label="E-mail", label="E-mail",
value=self.user['email'] if "@default.com" not in self.user['email'] else None, value=self.user['email'] if "@default.com" not in self.user['email'] else None,
read_only=True read_only=True if "@default.com" not in self.user['email'] else False
) )
self.phone = ft.TextField( self.phone = ft.TextField(
label="Telefon", label="Telefon",
value=self.user['phone'] if "@default.com" not in self.user['email'] else None value=self.user['phone'] if "@default.com" not in self.user['email'] else None
) )
address = ''
if self.user['address']:
if "~" in self.user['address']:
address = self.user['address'].split("~")[0].split("%")[1]
else:
address = self.user['address'].split("%")[1]
self.address = ft.TextField( self.address = ft.TextField(
label="Adresa", label="Strada si numar",
multiline=True, multiline=True,
min_lines=3, min_lines=3,
max_lines=5, max_lines=5,
value = self.user['address'].split("~")[0] if self.user['address'] else '' value = address
) )
city = ''
if self.user['address']:
if "~" in self.user['address']:
city = self.user['address'].split("~")[0].split("%")[0]
else:
city = self.user['address'].split("%")[0]
self.city = ft.TextField(
label="Oras",
value = city
)
self.company_name = ft.TextField( self.company_name = ft.TextField(
label="Denumire firma", label="Denumire firma",
value=self.company['name'] if self.company else '' value=self.company['name'] if self.company else ''
@@ -120,11 +156,11 @@ class Cart:
value=self.company['address'] if self.company else '') value=self.company['address'] if self.company else '')
self.second_address_placeholder = ft.Column() self.second_address_placeholder = ft.Column()
self.second_address = ft.TextField( self.second_address = ft.TextField(
label="Adresa de livrare", label="Adresa de livrare (str, nr, oras, judet)",
multiline=True, multiline=True,
min_lines=3, min_lines=3,
max_lines=5, max_lines=5,
value=self.user['address'].split("~")[1] if self.user['address'] and len(self.user['address'].split("~"))>1 else '' value=self.user['address'].split("~")[1] if self.user['address'] and "~" in self.user['address'] and len(self.user['address'].split("~"))>1 else ''
) )
self.second_address_placeholder =ft.Column() self.second_address_placeholder =ft.Column()
self.order_placeholder =ft.Column() self.order_placeholder =ft.Column()
@@ -145,10 +181,12 @@ class Cart:
"Detaili de livrare", "Detaili de livrare",
weight=ft.FontWeight.BOLD weight=ft.FontWeight.BOLD
), ),
self.user_name, self.first_name,
self.last_name,
self.email, self.email,
self.phone, self.phone,
self.address, self.address,
self.city,
ft.Divider(), ft.Divider(),
ft.Text("Adresa de livrare difera de adresa de domiciliu?", text_align=ft.TextAlign.CENTER), ft.Text("Adresa de livrare difera de adresa de domiciliu?", text_align=ft.TextAlign.CENTER),
ft.Button("Adauga adresa livrare", width=500, on_click=self.on_second_address_btn_click), ft.Button("Adauga adresa livrare", width=500, on_click=self.on_second_address_btn_click),
@@ -195,20 +233,22 @@ class Cart:
vertical_alignment=ft.CrossAxisAlignment.START vertical_alignment=ft.CrossAxisAlignment.START
) )
self.order_list_placeholder = ft.Column() self.order_list_placeholder = ft.Column()
if '@default.com' not in self.user['email']: self.order_list_placeholder.controls.append(self.orders_manager_list)
self.order_list_placeholder.controls.append(
ft.Text(
"Istoric comenzi",
weight=ft.FontWeight.BOLD
)
)
self.order_list_placeholder.controls.append(self.orders_list)
self.item_to_be_deleted = None self.item_to_be_deleted = None
self.page.add(self.delete_dialog) self.page.add(self.delete_dialog)
self.page.add(self.confirm_dialog) self.page.add(self.confirm_dialog)
discount_cards= self.card_manager.get_all_fidelity_cards()
_card_id = None
for card in discount_cards:
if card['phone_number'] == self.user['phone']:
_card_id = card['card_id']
break
self.card_id = ft.Text(f"Card de reducere nr. {_card_id}" if _card_id else '', weight=ft.FontWeight.BOLD)
def on_second_address_btn_click(self, e): def on_second_address_btn_click(self, e):
self.is_second_address = True self.is_second_address = True
self.second_address_placeholder.controls.append(self.second_address) self.second_address_placeholder.controls.append(self.second_address)
@@ -229,17 +269,27 @@ class Cart:
else: else:
pass pass
def order_products(self, id): def get_order_products(self, id):
products = self.orders.get_order_products(id) products = self.orders_manager.get_order_products(id)
total = 0
all_products = [] all_products = []
for product in products: for product in products:
name = self.productsDB.get(product['prdouct_id'])['name'] item = self.productsDB.get(product['prdouct_id'])
total += item['price']
name = item['name']
if name not in all_products: if name not in all_products:
all_products.append(name) all_products.append(name)
try:
self.pret_total.value = f'Pret total: {total}'
self.pret_total.update()
except:
pass
return ft.Text( return ft.Text(
value=' '.join(all_products) value=' '.join(all_products)
) )
def on_go_back_button_click(self, e): def on_go_back_button_click(self, e):
self.page.go("/") self.page.go("/")
@@ -296,6 +346,12 @@ class Cart:
] ]
def create_history_list(self, items): def create_history_list(self, items):
status_value = {
'new':'Noua',
'on_hold':'In asteptare',
'in_progress': 'In lucru',
'completed':'Complete'
}
return [ return [
ft.Container( ft.Container(
content=ft.Column( content=ft.Column(
@@ -303,7 +359,7 @@ class Cart:
ft.Row( ft.Row(
[ [
ft.Icon(ft.icons.ARROW_RIGHT, size=20), ft.Icon(ft.Icons.ARROW_RIGHT, size=20),
ft.Text(value="Numar comanda: "), ft.Text(value="Numar comanda: "),
ft.Text(value=item['id']) ft.Text(value=item['id'])
] ]
@@ -314,7 +370,7 @@ class Cart:
value='Status: ' value='Status: '
), ),
ft.Text( ft.Text(
value=item['status'] value=status_value[item['status']]
) )
] ]
), ),
@@ -323,7 +379,7 @@ class Cart:
ft.Text( ft.Text(
value='Produse: ' value='Produse: '
), ),
#self.order_products(item['id']) self.get_order_products(item['id'])
] ]
) )
], ],
@@ -338,7 +394,6 @@ class Cart:
for item in items for item in items
] ]
def on_delete_product_click(self, item): def on_delete_product_click(self, item):
print('Delte item', item) print('Delte item', item)
@@ -349,9 +404,9 @@ class Cart:
def on_confirm_delete_btn_click(self, e): def on_confirm_delete_btn_click(self, e):
print("confirm delete item", self.item_to_be_deleted) print("confirm delete item", self.item_to_be_deleted)
#remove item #remove item
self.orders.remove_product_from_order(self.on_hold_orders['id'], self.item_to_be_deleted) self.orders_manager.remove_product_from_order(self.on_hold_orders['id'], self.item_to_be_deleted)
#update list #update list
self.order_products = self.orders.get_order_products(self.on_hold_orders['id']) self.order_products = self.orders_manager.get_order_products(self.on_hold_orders['id'])
self.products = [] self.products = []
for product in self.order_products: for product in self.order_products:
self.products.append( self.products.append(
@@ -372,7 +427,6 @@ class Cart:
self.item_to_be_deleted = None self.item_to_be_deleted = None
self.page.close(self.delete_dialog) self.page.close(self.delete_dialog)
def open_confirm_dialog(self, e): def open_confirm_dialog(self, e):
print('open dialog') print('open dialog')
print(self.on_hold_orders) print(self.on_hold_orders)
@@ -392,18 +446,24 @@ class Cart:
self.error_message.value = "Va rugam inserati adresa de livrare!" self.error_message.value = "Va rugam inserati adresa de livrare!"
self.error_message.color = ft.Colors.RED self.error_message.color = ft.Colors.RED
self.error_message.update() self.error_message.update()
print('Second address found')
return True return True
print('Second address not found')
return False return False
def check_company_data_inserted(self, company_name, vat, register_number, company_address): def check_company_data_inserted(self, company_name, vat, register_number, company_address):
found = False found = False
if company_name is None or len(company_name)< 1: if company_name is None or len(company_name)< 1:
print("Comapny name not found")
found = True found = True
if vat is None or len(vat)< 1: if vat is None or len(vat)< 1:
print("Vat not found")
found = True found = True
if register_number is None or len(register_number)< 1: if register_number is None or len(register_number)< 1:
print("Register number not found")
found = True found = True
if company_address is None or len(company_address)< 1: if company_address is None or len(company_address)< 1:
print("Company address not found")
found = True found = True
if found: if found:
self.error_message.value = "Toate campurile sunt obligatori!" self.error_message.value = "Toate campurile sunt obligatori!"
@@ -411,13 +471,22 @@ class Cart:
self.error_message.update() self.error_message.update()
return found return found
def check_inserted_user_data(self, username, phone, address): def check_inserted_user_data(self, username, phone, address, email, city):
found = False found = False
if username is None or len(username)< 1: if username is None or len(username)< 1:
print('Username not found')
found = True found = True
if phone is None or len(phone)< 1: if phone is None or len(phone)< 1:
print("Phone not found")
found = True found = True
if address is None or len(address)< 1: if address is None or len(address)< 1:
print("Adress not found")
found = True
if city is None or len(city)< 1:
print("City not found")
found = True
if email is None or len(email)<1:
print("email not found")
found = True found = True
if found: if found:
self.error_message.value = "Toate campurile sunt obligatori!" self.error_message.value = "Toate campurile sunt obligatori!"
@@ -426,25 +495,53 @@ class Cart:
return found return found
def check_email_is_valid(self, email):
email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if re.fullmatch(email_regex, email) is not None:
self.error_message.value = ""
self.error_message.update()
return True
else:
self.error_message.value = "Va rugam inserati un e-mail valid!"
self.error_message.update()
return False
def create_update_user_details(self): def create_update_user_details(self):
username = self.user_name.value username = self.first_name.value + "~" + self.last_name.value
phone = self.phone.value phone = self.phone.value
address = self.address.value address = self.city.value+"%"+self.address.value
email = self.email.value
if self.is_second_address: if self.is_second_address:
print("Second address has been selected (button click)")
if self.check_second_address_inserted(self.second_address.value): if self.check_second_address_inserted(self.second_address.value):
return return False
address = self.address.value + '~' + self.second_address.value address = self.city.value+"%"+self.address.value + '~' + self.second_address.value
if self.check_inserted_user_data(username, phone, address): if self.check_inserted_user_data(username, phone, self.address.value, email, self.city.value):
return return False
if not self.check_email_is_valid(email):
return False
if '@default.com' in self.user['email']: if '@default.com' in self.user['email']:
self.user = self.user_manager.invite_user() print("User is default @default.com in name")
self.page.session.set("user", self.user) try:
user_id = self.user_manager.invite_user(
email, username, phone, address
)
self.user = self.user_manager.get(user_id)
print("User invited")
self.page.session.set("user", self.user)
self.orders_manager.update_order_user_id(user_id, self.on_hold_orders['id'])
except:
print("Unable to create user, user already exists")
self.error_message.value = "Exita deja un utilizator cu aceasta adresa de email!"
return False
else: else:
self.user_manager.update_user_data(username, phone, address, self.user['id']) self.user_manager.update_user_data(username, phone, address, self.user['id'])
if self.is_company: if self.is_company:
print("Compani selected (company button has been pressed)")
if self.check_company_data_inserted(self.company_name.value, self.vat.value, self.register_number.value, self.company_address.value): if self.check_company_data_inserted(self.company_name.value, self.vat.value, self.register_number.value, self.company_address.value):
return return
if self.company: if self.company:
@@ -462,36 +559,150 @@ class Cart:
'address': self.company_address.value 'address': self.company_address.value
} }
self.company_manager.add_company(company) self.company_manager.add_company(company)
if self.payment.value == None:
self.error_message.value = "Va rugam selectati metoda de plata!"
self.error_message.color = ft.Colors.RED
self.error_message.update()
return False
return True
def on_confim_btn_click(self, e): def create_history(self):
#create / update user details: if '@default.com' not in self.user['email']:
self.create_update_user_details() self.all_orders = self.orders_manager.get_orders_for_user(self.page.session.get('user')['id'])
self.all_orders = self.all_orders[::-1]
buffer = []
for order in self.all_orders:
if order['status'] != 'on_hold':
buffer.append(order)
self.orders_manager_list.controls.clear()
self.orders_manager_list.controls = self.create_history_list(buffer)
self.orders_manager_list.update()
print('confirm') def notify_admin_and_client(self):
self.page.close(self.confirm_dialog) users = self.user_manager.get_all()
self.orders.update_order_status("new", self.on_hold_orders['id']) admins = []
for user in users:
if user['role'] == 'admin':
admins.append(user)
for admin in admins:
send_gmail(
to_email=admin['email'],
subject="Comanda noua pe tainagustului.ro",
body=f'''
Ati primit o noua comanda de la {self.user['email']}.
Va rugam accesati wwww.tainagusutului.ro pentru detalii.
'''
)
self.products = [] send_gmail(
self.on_hold_orders = self.orders.get_on_hold_order(self.user['id']) to_email=self.user['email'],
if self.on_hold_orders: subject="Multumim pentru comanda!",
self.order_products = self.orders.get_order_products(self.on_hold_orders['id']) body=f'''
Buna ziua,
for product in self.order_products: Comanda a fost primita si va fi livrata in cel mai scurt timp.
self.products.append(
Va multumim,
Echipa tainagustului.ro
'''
)
def save_order_ntp_id(self, order_id, netopia_id):
self.netopia_manager.add_netopia_card(order_id, netopia_id)
def online_pay(self, order_id):
if self.payment.value == "plata_online_cu_cardul":
print("The user seelected card payment")
order_products = []
products_ids = self.orders_manager.get_order_products(order_id)
for prod in products_ids:
p = self.productsDB.get(prod['prdouct_id'])
order_products.append(
{ {
'product':self.productsDB.get(product['prdouct_id']), 'name':p['name'],
'quantity':product['quantity'] 'code':p['id'],
'category':p['category_id'],
'price':p['price'],
'vat':0,
} }
) )
self.product_list.controls.clear() print(order_products)
self.product_list.controls = self.create_list(self.products, self.on_delete_product_click) response = start_card_payment(
self.product_list.update() order_id=order_id,
if '@default.com' not in self.user['email']: amount=self.pret_total.value.split(": ")[1],
self.all_orders = self.orders.get_orders_for_user(self.page.session.get('user')['id']) currency='RON',
self.all_orders = self.all_orders[::-1] description="Comanda noua",
self.orders_list.controls.clear() customer={
self.orders_list.controls = self.create_history_list(self.all_orders) 'email':self.email.value,
self.orders_list.update() 'phone':self.phone.value,
'firstName':self.first_name.value,
'lastName':self.last_name.value,
'city':self.city.value,
'country': 642,
'address':self.address.value,
'county':'',
'zipCode':''
},
products=order_products
)
print(type(response))
# Extract URL & ntpID from SDK response
payment_url = response.payment['paymentURL']
ntp_id = response.payment['ntpID']
# 1) Persist mapping (VERY IMPORTANT for IPN/status reconciliation)
if ntp_id:
self.save_order_ntp_id(order_id, ntp_id) # implement in your DB layer
# 2) Open hosted payment page
if payment_url:
self.page.launch_url(payment_url, web_window_name="_blank")
self.page.go("/payment/redirect") # your UX page
else:
self.page.snack_bar = ft.SnackBar(ft.Text("Nu am primit URL-ul de plată."))
self.page.snack_bar.open = True
self.page.update()
def on_confim_btn_click(self, e):
self.error_message.color = ft.Colors.RED
self.error_message.update()
#create / update user details:
print("Confirm button Selected ")
self.page.close(self.confirm_dialog)
if self.create_update_user_details():
print('User details updated')
order_id = self.on_hold_orders['id']
self.orders_manager.update_order_status("new", self.on_hold_orders['id'])
print('Order status is set to new')
self.products = []
self.on_hold_orders = self.orders_manager.get_on_hold_order(self.user['id'])
if self.on_hold_orders:
self.order_products = self.orders_manager.get_order_products(self.on_hold_orders['id'])
for product in self.order_products:
self.products.append(
{
'product':self.productsDB.get(product['prdouct_id']),
'quantity':product['quantity']
}
)
self.product_list.controls.clear()
self.product_list.controls = self.create_list(self.products, self.on_delete_product_click)
self.product_list.update()
#hiostory
self.create_history()
self.error_message.value = "Comanda a fost trimisa cu success!"
self.error_message.color = ft.Colors.GREEN
self.error_message.update()
#online pay
self.online_pay(order_id)
#notify admin
self.notify_admin_and_client()
def build(self): def build(self):
return ft.Container( return ft.Container(
@@ -505,10 +716,16 @@ class Cart:
ft.Icons.SHOPPING_CART_CHECKOUT, ft.Icons.SHOPPING_CART_CHECKOUT,
size=40, size=40,
), ),
ft.Text( ft.Column(
"Produse adugate:", [
weight=ft.FontWeight.BOLD, self.pret_total,
size=15 self.card_id,
# ft.Text(
# "Produse adugate:",
# weight=ft.FontWeight.BOLD,
# size=15
# ),
]
) )
] ]
), ),

View File

@@ -0,0 +1,9 @@
import flet as ft
class ConfirmData:
def __init__(self):
self.first_name = ft.TextField(label="Prenume")
self.last_name = ft.TextField(label="Nume")
self.city = ft.TextField(label="Oras")
self.address = ft.TextField(label="Strada si numar")

View File

@@ -0,0 +1,35 @@
import flet as ft
class PaymentRedirect:
def __init__(self, page: ft.Page):
self.page = page
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Image(src='images/tainagustului.png', width=200)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text(
"Sunteți redirecționat către pagina de plată NETOPIA...",
size=20,
weight=ft.FontWeight.BOLD,
text_align=ft.TextAlign.CENTER,
),
ft.ProgressRing(width=40, height=40, color=ft.Colors.GREEN),
ft.Text(
"Vă rugăm să nu închideți această fereastră până la finalizarea plății.",
size=16,
color=ft.Colors.GREY_700,
text_align=ft.TextAlign.CENTER,
)
],
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
spacing=20
)
)

View File

@@ -1 +1,5 @@
flet==0.28.3 flet==0.28.3
netopia-sdk==2.1.1
python-dotenv==1.2.0
Flask==3.1.2
flask-cors==6.0.1