init commit

This commit is contained in:
2025-08-31 17:55:26 +03:00
commit 876ddec94a
78 changed files with 11999 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import os
from PyPDF2 import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
GENERATED_FOLDER = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "generated_pdfs")
)
def create_watermark_pdf(watermark_path, text="CANCELLED"):
c = canvas.Canvas(watermark_path, pagesize=A4)
c.setFont("Helvetica-Bold", 80)
c.setFillGray(0.6) # Light gray text
c.saveState()
c.translate(300, 400)
c.rotate(45)
c.drawCentredString(0, 0, text)
c.restoreState()
c.save()
def apply_watermark(input_pdf_path, output_pdf_path, watermark_pdf_path):
reader = PdfReader(input_pdf_path)
watermark = PdfReader(watermark_pdf_path).pages[0]
writer = PdfWriter()
for page in reader.pages:
page.merge_page(watermark)
writer.add_page(page)
with open(output_pdf_path, "wb") as f:
writer.write(f)
def cancel_order_pdf(order_filename):
# File paths
input_pdf_path = os.path.join(GENERATED_FOLDER, order_filename)
output_pdf_path = input_pdf_path
watermark_pdf_path = os.path.join(GENERATED_FOLDER, "temp_watermark.pdf")
print(watermark_pdf_path)
# Check if file exists
if not os.path.isfile(input_pdf_path):
raise FileNotFoundError(f"Original order PDF not found: {input_pdf_path}")
# Create watermark and apply it
create_watermark_pdf(watermark_pdf_path, text="CANCELLED")
apply_watermark(input_pdf_path, output_pdf_path, watermark_pdf_path)
# Optionally remove temp watermark
os.remove(watermark_pdf_path)
return output_pdf_path
# Example usage:
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Usage: python cancel_order.py <order_filename>")
else:
try:
result_path = cancel_order_pdf(sys.argv[1])
print(f"Cancelled PDF created: {result_path}")
except Exception as e:
print(f"Error: {e}")

View File

@@ -0,0 +1,139 @@
import smtplib
from email.message import EmailMessage
import os
def send_email(to_email, subject, body):
smtp_host = os.environ.get("SMTP_HOST")
smtp_port = int(os.environ.get("SMTP_PORT", 587))
smtp_user = os.environ.get("SMTP_USER")
smtp_pass = os.environ.get("SMTP_PASS")
sender_email = os.environ.get("SMTP_FROM", smtp_user)
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
raise ValueError("SMTP config incomplete in environment variables.")
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = sender_email
msg["To"] = to_email
msg.set_content(body)
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)
# Send email with attachment
def send_email_with_attachment(to_email, subject, body, attachment_path):
smtp_host = os.environ.get("SMTP_HOST")
smtp_port = int(os.environ.get("SMTP_PORT", 587))
smtp_user = os.environ.get("SMTP_USER")
smtp_pass = os.environ.get("SMTP_PASS")
sender_email = os.environ.get("SMTP_FROM", smtp_user)
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
raise ValueError("SMTP config incomplete in environment variables.")
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = sender_email
msg["To"] = to_email
msg.set_content(body)
if attachment_path and os.path.isfile(attachment_path):
with open(attachment_path, "rb") as f:
file_data = f.read()
file_name = os.path.basename(attachment_path)
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
else:
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)
# Send email using Gmail directly
def send_gmail(to_email, subject, body):
smtp_host = "smtp.gmail.com"
smtp_port = 587
smtp_user = 'macamete.robert@gmail.com'
smtp_pass = 'advx yqlv jkaa czvr'
sender_email = 'macamete.robert@gmail.com'
if not all([smtp_user, smtp_pass]):
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = sender_email
msg["To"] = to_email
msg.set_content(body)
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)
# Send email with attachment using Gmail directly
def send_gmail_with_attachment(to_email, subject, body, attachment_path):
smtp_host = "smtp.gmail.com"
smtp_port = 587
smtp_user = os.environ.get("GMAIL_USER")
smtp_pass = os.environ.get("GMAIL_PASS")
sender_email = smtp_user
if not all([smtp_user, smtp_pass]):
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = sender_email
msg["To"] = to_email
msg.set_content(body)
if attachment_path and os.path.isfile(attachment_path):
with open(attachment_path, "rb") as f:
file_data = f.read()
file_name = os.path.basename(attachment_path)
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
else:
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)
# Send email with attachment
def send_custom_email_with_attachment(to_email, subject, body, attachment_path, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS):
smtp_host = SMTP_HOST
smtp_port = int(SMTP_PORT)
smtp_user = SMTP_USER
smtp_pass = SMTP_PASS
sender_email = smtp_user
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
raise ValueError("SMTP config incomplete in environment variables.")
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = sender_email
msg["To"] = to_email
msg.set_content(body)
if attachment_path and os.path.isfile(attachment_path):
with open(attachment_path, "rb") as f:
file_data = f.read()
file_name = os.path.basename(attachment_path)
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
else:
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)

View File

@@ -0,0 +1,28 @@
from geopy.geocoders import Nominatim
class AdressCoordinates:
def __init__(self, address):
self.addess = address
def open_Maps_by_address(self):
address = self.addess
if not address:
return
#try:
geolocator = Nominatim(user_agent="flet_Maps_app")
location = geolocator.geocode(address)
print(location)
if location:
latitude = location.latitude
longitude = location.longitude
Maps_url = f"https://www.google.com/maps/search/?api=1&query={latitude},{longitude}"
return {
'latitude' : latitude,
'longitude' : longitude,
'Maps_url': Maps_url
}
#except Exception as ex:
# print(ex)

View File

@@ -0,0 +1,199 @@
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.platypus import Table, TableStyle, Paragraph, SimpleDocTemplate, Spacer, Image
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet
import io
import os
import logging
# --- helpers ---------------------------------------------------------------
def _resolve_logo_path(logo_path: str | None) -> str | None:
"""Return a readable logo path or None. Tries multiple fallbacks.
This avoids crashes when a caller passes a path that exists only in the
client container (e.g., '/client/assets/...') while we're running in the
server container on Fly.
"""
here = os.path.dirname(__file__)
project_root = os.path.abspath(os.path.join(here, "..", ".."))
candidates = []
# 1) if caller provided a path, try it as-is
if logo_path:
candidates.append(logo_path)
# also try without a leading slash inside our image tree
stripped = logo_path.lstrip("/\\")
candidates.append(os.path.join(project_root, stripped))
# 2) Known locations in this repo layout
candidates.append(os.path.join(project_root, "client", "assets", "images", "truck_logo_black.png"))
candidates.append(os.path.join(project_root, "assets", "images", "truck_logo_black.png"))
# 3) Allow override via env
env_path = os.getenv("DEFAULT_LOGO_PATH")
if env_path:
candidates.insert(0, env_path)
for p in candidates:
try:
if p and os.path.isfile(p):
return p
except Exception:
# some paths may be malformed on certain platforms
continue
return None
def generate_order_pdf(order, user_data, transporter_data, logo_path, save_to_disk=True):
#print(order)
#print(f'user data: {user_data}')
if 'address' in user_data:
address = user_data.get("address")
if address:
address = address.replace(" %",", ")
else:
address = ''
else:
address=''
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=A4)
elements = []
styles = getSampleStyleSheet()
# Resolve logo path robustly across local/dev and Fly
logo_path = _resolve_logo_path(logo_path)
# Prepare texts
user_text = f"""<b>{user_data.get("name", "")}</b><br/>
{user_data.get("register_number", "")}<br/>
{user_data.get("vat", "")}<br/>
{address}<br/>
{""}<br/>
{user_data.get("contact_name", "")}<br/>
{user_data.get("phone", "")}<br/>
{user_data.get("email", "")}
"""
transporter_text = f"""<b>{transporter_data.get("name", "")}</b><br/>
{transporter_data.get("contact_person", "")}<br/>
{transporter_data.get("phone", "")}<br/>
{transporter_data.get("email", "")}
"""
# Logo (centered), tolerate missing file
logo = None
if logo_path:
try:
logo = Image(logo_path, width=120, mask='auto', height=60)
logo.hAlign = 'CENTER'
except Exception as e:
logging.warning("PDF: failed to load logo at %s: %s", logo_path, e)
logo = None
# Top section: transporter - logo - user
top_section = Table([
[
Paragraph(transporter_text, styles['Normal']),
logo,
Paragraph(user_text, styles['Normal'])
]
], colWidths=[200, 150, 200])
top_section.setStyle(TableStyle([
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ALIGN", (0, 0), (0, 0), "LEFT"),
("ALIGN", (1, 0), (1, 0), "CENTER"),
("ALIGN", (2, 0), (2, 0), "RIGHT"),
("LEFTPADDING", (0, 0), (0, 0), 20),
]))
elements.append(top_section)
elements.append(Spacer(1, 12))
# Order number and current date (centered, vertically stacked)
from datetime import datetime
header_info = Table([
[Paragraph(f"<b>Loading Order</b>: {order['order_number']}", styles["Normal"])],
[Paragraph(f"Date: {datetime.now().strftime('%d/%m/%Y')}", styles["Normal"])]
], colWidths=[450])
header_info.setStyle(TableStyle([
("ALIGN", (0, 0), (-1, -1), "CENTER")
]))
elements.append(header_info)
elements.append(Spacer(1, 12))
# Order Summary Table
elements.append(Paragraph("Summary", styles['Heading3']))
summary_data = [
["Details", Paragraph("Values", styles["Normal"])],
["Truck Reg. No.", Paragraph(str(order["track_reg_number"]), styles["Normal"])],
["Trailer Reg. No.", Paragraph(str(order["trailer_reg_number"]), styles["Normal"])],
["Product Description", Paragraph(str(order["products_description"]), styles["Normal"])],
["LDM", Paragraph(str(order["ldb_quantity"]), styles["Normal"])],
["KG", Paragraph(str(order["kg_quantity"]), styles["Normal"])],
["Price", Paragraph(str(order["paid_price"]), styles["Normal"])],
]
summary_table = Table(summary_data, colWidths=[150, 350])
summary_table.setStyle(TableStyle([
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("WORDWRAP", (0, 0), (-1, -1), "CJK"),
("ALIGN", (0, 0), (-1, -1), "LEFT"),
]))
elements.append(summary_table)
elements.append(Spacer(1, 24))
# Loading Points
elements.append(Paragraph("Loading Sequence", styles['Heading3']))
loading_data = [[Paragraph("Address", styles["Normal"]), Paragraph("Date & Hour", styles["Normal"]), Paragraph("Instructions", styles["Normal"])]]
for l in order["loading_addresses"]:
loading_data.append([
Paragraph(f"{l["loading_address_name"]}: {l["loading_address"]}", styles["Normal"]),
Paragraph(f"{l['loading_date']} {l['loading_hour']}", styles["Normal"]),
Paragraph(str(l["loading_informatins"]), styles["Normal"])
])
loading_table = Table(loading_data, colWidths=[200, 100, 200])
loading_table.setStyle(TableStyle([
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("WORDWRAP", (0, 0), (-1, -1), "CJK"),
("ALIGN", (0, 0), (-1, -1), "LEFT"),
]))
elements.append(loading_table)
elements.append(Spacer(1, 24))
# Unloading Points
elements.append(Paragraph("Unloading Sequence", styles['Heading3']))
unloading_data = [[Paragraph("Address", styles["Normal"]), Paragraph("Date & Hour", styles["Normal"]), Paragraph("Instructions", styles["Normal"])]]
for u in order["unloading_addresses"]:
unloading_data.append([
Paragraph(f"{u["unloading_address_name"]}: {u["unloading_address"]}", styles["Normal"]),
Paragraph(f"{u['unloading_date']} {u['unloading_hour']}", styles["Normal"]),
Paragraph(str(u["unloading_informatins"]), styles["Normal"])
])
unloading_table = Table(unloading_data, colWidths=[200, 100, 200])
unloading_table.setStyle(TableStyle([
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("WORDWRAP", (0, 0), (-1, -1), "CJK"),
("ALIGN", (0, 0), (-1, -1), "LEFT"),
]))
elements.append(unloading_table)
elements.append(Spacer(1, 24))
elements.append(Paragraph("Terms and Conditions", styles["Heading3"]),)
elements.append(
Paragraph(str(order["terms"]), styles["Normal"])
)
doc.build(elements)
buffer.seek(0)
if save_to_disk:
save_path=f"generated_pdfs/order_{order['user_id']}_{order['order_number']}.pdf"
with open(save_path, "wb") as f:
f.write(buffer.getvalue())
return buffer