#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Generator documente livrabile PNRR - Platforma DiDi TOP CLOSSERS SRL | CUI: 36193026 Proiect: PNRR Contract 11.1.i3.c9/10.05.2024 Genereaza 4 documente DOCX profesionale: 1. Manual_Utilizare_Website.docx 2. Ghid_Administrare_ERPNext.docx 3. Documentatie_API.docx 4. Arhitectura_Detaliata.docx """ import sys import os # Force UTF-8 output if sys.stdout.encoding != "utf-8": sys.stdout.reconfigure(encoding="utf-8") from docx import Document from docx.shared import Inches, Pt, Cm, RGBColor, Emu from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.table import WD_TABLE_ALIGNMENT from docx.enum.section import WD_ORIENT from docx.oxml.ns import qn, nsdecls from docx.oxml import parse_xml import datetime # ============================================================================== # CONFIGURATION # ============================================================================== BASE_DIR = os.path.dirname(os.path.abspath(__file__)) SCREENSHOTS_DIR = os.path.join(BASE_DIR, "screenshots") OUTPUT_DIR = BASE_DIR COMPANY = { "name": "TOP CLOSSERS SRL", "cui": "36193026", "address": "Str. Targovistei 15, Ploiesti, 100299, Prahova", "representative": "Alexe Vasile-Emilian", "project_ref": "PNRR Contract 11.1.i3.c9/10.05.2024", "platform": "DiDi - Platforma Digitala Inteligenta pentru Prevenirea si Combaterea Dezinformarii", "date": "Aprilie 2026", } HEADER_TEXT = "DiDi - Platforma Digitala Inteligenta" FOOTER_TEXT = "TOP CLOSSERS SRL | CUI: 36193026 | Confidential" # ============================================================================== # HELPER FUNCTIONS # ============================================================================== def create_document(): """Create a new document with base styles configured.""" doc = Document() # Configure default font style = doc.styles["Normal"] font = style.font font.name = "Calibri" font.size = Pt(11) font.color.rgb = RGBColor(0x33, 0x33, 0x33) style.paragraph_format.space_after = Pt(6) style.paragraph_format.line_spacing = 1.15 # Configure heading styles for level, (size, color, bold) in { "Heading 1": (Pt(20), RGBColor(0x1A, 0x3C, 0x6E), True), "Heading 2": (Pt(16), RGBColor(0x2B, 0x57, 0x9A), True), "Heading 3": (Pt(13), RGBColor(0x3A, 0x6E, 0xAF), True), }.items(): h_style = doc.styles[level] h_style.font.name = "Calibri" h_style.font.size = size h_style.font.color.rgb = color h_style.font.bold = bold h_style.paragraph_format.space_before = Pt(18 if level == "Heading 1" else 12) h_style.paragraph_format.space_after = Pt(8) # Set margins for section in doc.sections: section.top_margin = Cm(2.5) section.bottom_margin = Cm(2.5) section.left_margin = Cm(2.5) section.right_margin = Cm(2.5) return doc def add_header_footer(doc): """Add header and footer to all sections.""" for section in doc.sections: # Header header = section.header header.is_linked_to_previous = False hp = header.paragraphs[0] if header.paragraphs else header.add_paragraph() hp.text = HEADER_TEXT hp.alignment = WD_ALIGN_PARAGRAPH.RIGHT run = hp.runs[0] if hp.runs else hp.add_run() run.font.size = Pt(9) run.font.color.rgb = RGBColor(0x66, 0x66, 0x66) run.font.italic = True # Footer footer = section.footer footer.is_linked_to_previous = False fp = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph() fp.text = FOOTER_TEXT fp.alignment = WD_ALIGN_PARAGRAPH.CENTER run = fp.runs[0] if fp.runs else fp.add_run() run.font.size = Pt(8) run.font.color.rgb = RGBColor(0x99, 0x99, 0x99) def add_cover_page(doc, title, subtitle=None): """Add a professional cover page.""" # Add empty paragraphs for spacing for _ in range(4): p = doc.add_paragraph() p.paragraph_format.space_after = Pt(0) # Main title p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER run = p.add_run(title) run.bold = True run.font.size = Pt(28) run.font.color.rgb = RGBColor(0x1A, 0x3C, 0x6E) run.font.name = "Calibri" if subtitle: p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER run = p.add_run(subtitle) run.font.size = Pt(16) run.font.color.rgb = RGBColor(0x2B, 0x57, 0x9A) run.font.name = "Calibri" # Separator line doc.add_paragraph() p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER run = p.add_run("_" * 60) run.font.color.rgb = RGBColor(0xCC, 0xCC, 0xCC) doc.add_paragraph() # Project info table info_data = [ ("Beneficiar", COMPANY["name"]), ("CUI", COMPANY["cui"]), ("Adresa", COMPANY["address"]), ("Reprezentant Legal", COMPANY["representative"]), ("Referinta Proiect", COMPANY["project_ref"]), ("Platforma", COMPANY["platform"]), ("Data", COMPANY["date"]), ] table = doc.add_table(rows=len(info_data), cols=2) table.alignment = WD_TABLE_ALIGNMENT.CENTER table.style = "Table Grid" for i, (label, value) in enumerate(info_data): cell_label = table.rows[i].cells[0] cell_value = table.rows[i].cells[1] # Label cell p = cell_label.paragraphs[0] run = p.add_run(label) run.bold = True run.font.size = Pt(10) run.font.name = "Calibri" run.font.color.rgb = RGBColor(0x1A, 0x3C, 0x6E) # Value cell p = cell_value.paragraphs[0] run = p.add_run(value) run.font.size = Pt(10) run.font.name = "Calibri" # Style cells for cell in [cell_label, cell_value]: tc = cell._tc tcPr = tc.get_or_add_tcPr() shading = parse_xml(f'') tcPr.append(shading) # Set column widths for row in table.rows: row.cells[0].width = Inches(2.2) row.cells[1].width = Inches(4.0) # Add page break after cover doc.add_page_break() def add_screenshot(doc, subfolder, filename, caption=None, width=Inches(6)): """Add a screenshot image with caption. Skip gracefully if not found.""" img_path = os.path.join(SCREENSHOTS_DIR, subfolder, filename) if not os.path.exists(img_path): p = doc.add_paragraph() run = p.add_run(f"[Captura de ecran: {filename} - fisier indisponibil]") run.italic = True run.font.color.rgb = RGBColor(0x99, 0x99, 0x99) run.font.size = Pt(9) return try: doc.add_picture(img_path, width=width) # Center the image last_paragraph = doc.paragraphs[-1] last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER if caption: p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER run = p.add_run(f"Figura: {caption}") run.italic = True run.font.size = Pt(9) run.font.color.rgb = RGBColor(0x66, 0x66, 0x66) p.paragraph_format.space_after = Pt(12) except Exception as e: p = doc.add_paragraph() run = p.add_run(f"[Eroare la incarcarea capturii: {filename} - {str(e)}]") run.italic = True run.font.color.rgb = RGBColor(0xCC, 0x00, 0x00) run.font.size = Pt(9) def add_table_with_header(doc, headers, rows, col_widths=None): """Add a formatted table with header row.""" table = doc.add_table(rows=1 + len(rows), cols=len(headers)) table.style = "Table Grid" table.alignment = WD_TABLE_ALIGNMENT.CENTER # Header row for i, header in enumerate(headers): cell = table.rows[0].cells[i] p = cell.paragraphs[0] p.alignment = WD_ALIGN_PARAGRAPH.CENTER run = p.add_run(header) run.bold = True run.font.size = Pt(9) run.font.name = "Calibri" run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF) # Dark blue background tc = cell._tc tcPr = tc.get_or_add_tcPr() shading = parse_xml(f'') tcPr.append(shading) # Data rows for row_idx, row_data in enumerate(rows): for col_idx, value in enumerate(row_data): cell = table.rows[row_idx + 1].cells[col_idx] p = cell.paragraphs[0] run = p.add_run(str(value)) run.font.size = Pt(9) run.font.name = "Calibri" # Alternate row shading if row_idx % 2 == 0: tc = cell._tc tcPr = tc.get_or_add_tcPr() shading = parse_xml( f'' ) tcPr.append(shading) # Set column widths if specified if col_widths: for row in table.rows: for i, width in enumerate(col_widths): if i < len(row.cells): row.cells[i].width = width doc.add_paragraph() # Spacing after table return table def add_bullet_list(doc, items, level=0): """Add a bulleted list.""" for item in items: p = doc.add_paragraph(style="List Bullet") run = p.add_run(item) run.font.size = Pt(10) run.font.name = "Calibri" if level > 0: p.paragraph_format.left_indent = Cm(1.5 * level) def add_numbered_list(doc, items): """Add a numbered list.""" for item in items: p = doc.add_paragraph(style="List Number") run = p.add_run(item) run.font.size = Pt(10) run.font.name = "Calibri" def add_note_box(doc, text, note_type="info"): """Add a styled note/info box using a single-cell table.""" colors = { "info": ("E8F0FE", "1A3C6E"), "warning": ("FFF3CD", "856404"), "tip": ("D4EDDA", "155724"), } bg_color, text_color = colors.get(note_type, colors["info"]) table = doc.add_table(rows=1, cols=1) table.style = "Table Grid" cell = table.rows[0].cells[0] # Set background tc = cell._tc tcPr = tc.get_or_add_tcPr() shading = parse_xml( f'' ) tcPr.append(shading) p = cell.paragraphs[0] prefix = {"info": "NOTA: ", "warning": "ATENTIE: ", "tip": "SFAT: "}.get( note_type, "" ) run = p.add_run(prefix) run.bold = True run.font.size = Pt(9) run.font.color.rgb = RGBColor( int(text_color[0:2], 16), int(text_color[2:4], 16), int(text_color[4:6], 16) ) run = p.add_run(text) run.font.size = Pt(9) run.font.color.rgb = RGBColor( int(text_color[0:2], 16), int(text_color[2:4], 16), int(text_color[4:6], 16) ) doc.add_paragraph() # Spacing def add_code_block(doc, code_text): """Add a code block with monospace font and gray background.""" table = doc.add_table(rows=1, cols=1) table.style = "Table Grid" cell = table.rows[0].cells[0] tc = cell._tc tcPr = tc.get_or_add_tcPr() shading = parse_xml(f'') tcPr.append(shading) p = cell.paragraphs[0] for line in code_text.strip().split("\n"): run = p.add_run(line + "\n") run.font.name = "Consolas" run.font.size = Pt(8.5) run.font.color.rgb = RGBColor(0x2D, 0x2D, 0x2D) doc.add_paragraph() def add_toc_placeholder(doc): """Add a Table of Contents placeholder heading.""" doc.add_heading("Cuprins", level=1) p = doc.add_paragraph() run = p.add_run( "[Cuprinsul se genereaza automat in Microsoft Word: References > Table of Contents > Update Table]" ) run.italic = True run.font.size = Pt(10) run.font.color.rgb = RGBColor(0x99, 0x99, 0x99) # Insert TOC field paragraph = doc.add_paragraph() run = paragraph.add_run() fldChar = parse_xml(f'') run._r.append(fldChar) run = paragraph.add_run() instrText = parse_xml( f' TOC \\o "1-3" \\h \\z \\u ' ) run._r.append(instrText) run = paragraph.add_run() fldChar = parse_xml(f'') run._r.append(fldChar) run = paragraph.add_run("Actualizati cuprinsul dupa deschiderea documentului.") run.font.color.rgb = RGBColor(0x99, 0x99, 0x99) run.font.italic = True run = paragraph.add_run() fldChar = parse_xml(f'') run._r.append(fldChar) doc.add_page_break() # ============================================================================== # DOCUMENT 1: Manual Utilizare Website # ============================================================================== def generate_manual_utilizare(): """Generate Manual_Utilizare_Website.docx""" print("[1/4] Generare Manual_Utilizare_Website.docx ...") doc = create_document() # Cover page add_cover_page( doc, "MANUAL DE UTILIZARE", "Website DiDi cu E-commerce", ) add_header_footer(doc) add_toc_placeholder(doc) # ========================================================================= # CHAPTER 1: Introducere # ========================================================================= doc.add_heading("1. Introducere", level=1) doc.add_heading("1.1 Prezentarea Platformei DiDi", level=2) p = doc.add_paragraph() p.add_run( "DiDi (Platforma Digitala Inteligenta pentru Prevenirea si Combaterea Dezinformarii) " "este o solutie web inovatoare dezvoltata in cadrul proiectului PNRR Contract 11.1.i3.c9/10.05.2024 " "de catre TOP CLOSSERS SRL. Platforma ofera instrumente avansate bazate pe inteligenta artificiala " "pentru detectarea si analiza dezinformarii in continut digital." ) p = doc.add_paragraph() p.add_run( "Platforma DiDi combina tehnologii de ultima generatie in procesarea limbajului natural (NLP), " "detectia deepfake-urilor, verificarea faptelor (fact-checking), evaluarea credibilitatii surselor " "si identificarea tehnicilor de manipulare. Aceste capabilitati sunt disponibile prin intermediul " "unui website modern, intuitiv si accesibil, integrat cu un sistem de e-commerce care permite " "achizitionarea serviciilor de analiza in mod simplu si securizat." ) doc.add_heading("1.2 Scopul Documentului", level=2) p = doc.add_paragraph() p.add_run( "Prezentul manual de utilizare are ca scop principal furnizarea instructiunilor detaliate pentru " "utilizarea website-ului DiDi de catre toate categoriile de utilizatori. Documentul acopera:" ) add_bullet_list(doc, [ "Navigarea pe paginile publice ale website-ului", "Procesul de inregistrare si autentificare", "Utilizarea dashboard-ului client pentru achizitionarea si rularea analizelor", "Procesul de plata prin Stripe", "Vizualizarea facturilor si gestionarea profilului", "Functionalitati tehnice suplimentare (responsive design, multilingvism, SEO)", ]) doc.add_heading("1.3 Audienta Tinta", level=2) p = doc.add_paragraph() p.add_run("Acest manual se adreseaza urmatoarelor categorii de utilizatori:") add_bullet_list(doc, [ "Clienti finali - persoane fizice sau juridice care doresc sa utilizeze serviciile de analiza DiDi pentru verificarea continutului digital", "Administratori de continut - personal responsabil cu gestionarea informatiilor de pe website", "Personal tehnic de suport - echipa care ofera asistenta utilizatorilor platformei", "Evaluatori PNRR - persoane implicate in verificarea si evaluarea livrabilelor proiectului", ]) doc.add_heading("1.4 Conventii Utilizate in Document", level=2) p = doc.add_paragraph() p.add_run("In cadrul acestui manual se utilizeaza urmatoarele conventii:") add_bullet_list(doc, [ "Textul bold indica elemente de interfata (butoane, meniuri, etichete)", "Textul italic indica note importante sau observatii", "Capturile de ecran ilustreaza starea reala a interfetei la momentul documentarii", "Casetele colorate contin note, avertismente sau sfaturi utile", ]) doc.add_page_break() # ========================================================================= # CHAPTER 2: Accesare si Navigare # ========================================================================= doc.add_heading("2. Accesare si Navigare", level=1) doc.add_heading("2.1 Accesarea Website-ului", level=2) p = doc.add_paragraph() p.add_run( "Website-ul DiDi este accesibil prin intermediul unui browser web modern (Google Chrome, Mozilla Firefox, " "Microsoft Edge, Safari) la urmatoarea adresa URL:" ) add_code_block(doc, "https://didi365.eu") p = doc.add_paragraph() p.add_run( "Website-ul este optimizat pentru cele mai recente versiuni ale browserelor mentionate si suporta " "atat conexiuni desktop cat si dispozitive mobile (tableta, smartphone). Conexiunea este securizata " "prin protocol HTTPS cu certificat TLS valid." ) doc.add_heading("2.2 Structura de Navigare", level=2) p = doc.add_paragraph() p.add_run( "Bara de navigare principala (navbar) este afisata in partea superioara a fiecarei pagini si contine " "urmatoarele elemente:" ) add_table_with_header( doc, ["Element", "Descriere", "Pozitie"], [ ["Logo DiDi", "Logo-ul platformei, click redirectioneaza la homepage", "Stanga"], ["Servicii", "Link catre pagina de prezentare a serviciilor de analiza", "Centru"], ["Despre", "Link catre pagina cu informatii despre companie si platforma", "Centru"], ["Pricing", "Link catre pagina cu preturile serviciilor", "Centru"], ["Contact", "Link catre formularul de contact", "Centru"], ["Language Switcher", "Comutator RO/EN pentru schimbarea limbii interfetei", "Dreapta"], ["Autentificare / Dashboard", "Buton de login sau acces la dashboard (dupa autentificare)", "Dreapta"], ], col_widths=[Inches(1.5), Inches(3.5), Inches(1.0)], ) doc.add_heading("2.3 Footer", level=2) p = doc.add_paragraph() p.add_run( "Footer-ul website-ului contine informatii suplimentare organizate in sectiuni:" ) add_bullet_list(doc, [ "Linkuri rapide catre paginile principale (Servicii, Despre, Pricing, Contact)", "Linkuri legale: Politica de Confidentialitate, Termeni si Conditii", "Informatii de contact: adresa email, telefon", "Copyright si informatii companie: TOP CLOSSERS SRL", "Referinta proiect PNRR", ]) doc.add_heading("2.4 Selectarea Limbii (Language Switcher)", level=2) p = doc.add_paragraph() p.add_run( "Website-ul DiDi este disponibil in doua limbi: Romana (RO) si Engleza (EN). " "Utilizatorul poate comuta intre cele doua limbi folosind selectorul de limba " "din coltul din dreapta sus al barei de navigare. Preferinta de limba se salveaza " "in localStorage-ul browserului si se pastreaza la vizitele ulterioare." ) add_screenshot( doc, "website", "01-homepage.png", "Pagina principala DiDi - Navigare si structura generala" ) doc.add_page_break() # ========================================================================= # CHAPTER 3: Pagini Publice # ========================================================================= doc.add_heading("3. Pagini Publice", level=1) p = doc.add_paragraph() p.add_run( "Website-ul DiDi ofera mai multe pagini publice accesibile fara autentificare, " "care prezinta informatii despre platforma, serviciile disponibile si modalitatile de contact." ) # 3.1 Homepage doc.add_heading("3.1 Homepage (Pagina Principala)", level=2) p = doc.add_paragraph() p.add_run( "Pagina principala este punctul de intrare al website-ului si ofera o prezentare " "completa a platformei DiDi. Este structurata in mai multe sectiuni verticale, " "fiecare proiectata pentru a comunica clar valoarea si capabilitatile platformei." ) doc.add_heading("Hero Section", level=3) p = doc.add_paragraph() p.add_run( 'Sectiunea principala afiseaza branding-ul "by Clossers / didi" impreuna cu ' "un mesaj concis despre misiunea platformei de combatere a dezinformarii. " "Include un buton CTA (Call-to-Action) care directioneaza utilizatorul catre " "sectiunea de servicii sau catre inregistrare." ) doc.add_heading("Sectiunea Despre Platforma", level=3) p = doc.add_paragraph() p.add_run( "Prezinta pe scurt scopul platformei DiDi si contextul in care a fost dezvoltata " "(proiect PNRR pentru digitalizarea luptei impotriva dezinformarii). Se evidentiaza " "caracterul inovator al solutiei si tehnologiile AI utilizate." ) doc.add_heading("Sectiunea Capabilitati", level=3) p = doc.add_paragraph() p.add_run( "Afiseaza cele 6 module principale de analiza AI disponibile in platforma, fiecare " "cu un icon reprezentativ, titlu si descriere scurta. Modulele acoperite sunt: " "Analiza Text/NLP, Detectie Deepfake, Fact-Checking, Evaluare Surse, " "Detectie Tehnici de Manipulare si Monitorizare in Timp Real." ) doc.add_heading("Cum Functioneaza - 4 Pasi", level=3) p = doc.add_paragraph() p.add_run( "Aceasta sectiune prezinta procesul de utilizare a platformei in 4 pasi simpli:" ) add_numbered_list(doc, [ "Creaza un cont - Inregistrare rapida prin sistemul de autentificare securizat Keycloak SSO", "Achizitioneaza o analiza - Selecteaza tipul de analiza dorit si efectueaza plata prin Stripe", "Incarca continutul - Introdu textul, URL-ul sau incarca fisierul media (imagine, audio, video)", "Primeste rezultatele - Obtine un raport detaliat cu scorul de risc, incredere si recomandari", ]) doc.add_heading("Cazuri de Utilizare", level=3) p = doc.add_paragraph() p.add_run( "Sectiunea prezinta scenarii concrete in care platforma DiDi poate fi utilizata: " "verificarea stirilor online, analiza continutului viral pe retele sociale, " "detectarea imaginilor sau videoclipurilor manipulate, evaluarea credibilitatii " "surselor de informatii si monitorizarea campaniilor de dezinformare." ) doc.add_heading("Call-to-Action Final", level=3) p = doc.add_paragraph() p.add_run( "Pagina se incheie cu o sectiune CTA care invita utilizatorii sa creeze un cont " "si sa inceapa sa utilizeze platforma, oferind un buton de inregistrare prominent." ) add_screenshot(doc, "website", "01-homepage.png", "Homepage - Pagina principala a website-ului DiDi") doc.add_page_break() # 3.2 Servicii doc.add_heading("3.2 Servicii", level=2) p = doc.add_paragraph() p.add_run( "Pagina de Servicii prezinta in detaliu cele 6 module de analiza AI disponibile " "in platforma DiDi. Fiecare modul este descris cu functionalitati specifice, " "tipuri de continut suportate si beneficii pentru utilizator." ) doc.add_heading("Modulele de Analiza AI", level=3) services_data = [ ("Analiza Text / NLP", "Procesare avansata a limbajului natural pentru detectarea elementelor de dezinformare in texte. Identifica manipulari lingvistice, bias-uri si inconsistente factuale."), ("Detectie Deepfake", "Algoritmi de inteligenta artificiala pentru detectarea continutului media manipulat (imagini, audio, video). Identifica semne de alterare digitala si generare AI."), ("Fact-Checking", "Verificare automata a afirmatiilor din continut prin comparare cu surse credibile si baze de date verificate. Evalueaza veridicitatea informatiilor."), ("Evaluare Surse", "Analiza credibilitatii si reputatiei surselor de informatii. Evalueaza domenii web, publicatii si autori pe baza unui scoring multi-criterial."), ("Detectie Tehnici de Manipulare", "Identificarea tehnicilor specifice de manipulare si propaganda utilizate in continut: apel la emotie, cherry-picking, whataboutism, etc."), ("Monitorizare in Timp Real", "Monitorizare continua a fluxurilor de informatii pentru identificarea rapida a campaniilor de dezinformare si a trendurilor de manipulare."), ] for name, desc in services_data: doc.add_heading(name, level=3) p = doc.add_paragraph() p.add_run(desc) p = doc.add_paragraph() p.add_run( "Fiecare serviciu este disponibil pentru diferite tipuri de media: text, imagine, audio, video " "si URL. Preturile variaza in functie de complexitatea analizei si tipul de continut procesat." ) add_screenshot(doc, "website", "02-services.png", "Pagina Servicii - Modulele de analiza AI DiDi") doc.add_page_break() # 3.3 Despre doc.add_heading("3.3 Despre", level=2) p = doc.add_paragraph() p.add_run( "Pagina Despre ofera informatii detaliate despre compania Clossers si platforma DiDi. " "Sectiunea prezinta misiunea companiei, viziunea pe termen lung in domeniul combaterii " "dezinformarii si echipa de specialisti implicata in dezvoltarea platformei." ) p = doc.add_paragraph() p.add_run( "Se evidentiaza contextul dezvoltarii platformei in cadrul programului PNRR, obiectivele " "urmarite si impactul asteptat asupra ecosistemului informational din Romania. Pagina include " "de asemenea informatii despre metodologia de lucru si tehnologiile utilizate." ) add_screenshot(doc, "website", "03-about.png", "Pagina Despre - Informatii companie si echipa") # 3.4 Pricing doc.add_heading("3.4 Pricing (Preturi)", level=2) p = doc.add_paragraph() p.add_run( "Pagina Pricing afiseaza preturile serviciilor de analiza organizate pe 4 categorii de servicii " "si tipuri de media. Preturile sunt exprimate in RON (lei romanesti) si includ TVA." ) doc.add_heading("Categorii de Servicii si Preturi", level=3) add_table_with_header( doc, ["Categorie", "Text", "Imagine", "Audio", "Video", "URL"], [ ["Detectie Tehnici Manipulare", "9,99 RON", "14,99 RON", "19,99 RON", "24,99 RON", "9,99 RON"], ["AI Detection (Deepfake)", "9,99 RON", "14,99 RON", "19,99 RON", "24,99 RON", "9,99 RON"], ["Fact-Checking", "9,99 RON", "14,99 RON", "19,99 RON", "24,99 RON", "9,99 RON"], ["Evaluare Surse", "9,99 RON", "14,99 RON", "19,99 RON", "24,99 RON", "9,99 RON"], ], ) p = doc.add_paragraph() p.add_run( "Toate serviciile sunt de tip one-time (plata unica per analiza). Fiecare achizitie ofera " "dreptul de a efectua o singura analiza pe tipul de continut si categoria selectata. " "Serviciile achizitionate nu expira si pot fi utilizate oricand." ) add_screenshot(doc, "website", "04-pricing.png", "Pagina Pricing - Preturi servicii analiza") doc.add_page_break() # 3.5 Contact doc.add_heading("3.5 Contact", level=2) p = doc.add_paragraph() p.add_run( "Pagina de Contact pune la dispozitia vizitatorilor un formular simplu si intuitiv " "pentru transmiterea mesajelor catre echipa DiDi. Formularul contine urmatoarele campuri:" ) add_table_with_header( doc, ["Camp", "Tip", "Obligatoriu", "Descriere"], [ ["Nume", "Text", "Da", "Numele complet al persoanei de contact"], ["Email", "Email", "Da", "Adresa de email pentru raspuns"], ["Mesaj", "Textarea", "Da", "Continutul mesajului (intrebare, solicitare, feedback)"], ], ) p = doc.add_paragraph() p.add_run( "La trimiterea formularului, datele sunt procesate automat si se creaza un Lead in sistemul " "CRM ERPNext. Acest mecanism asigura ca fiecare solicitare este inregistrata si urmarita " "de echipa de vanzari sau suport. Formularul suporta si parametri UTM pentru tracking-ul " "sursei vizitatorului (sursa, mediu, campanie)." ) add_note_box( doc, "Toate datele transmise prin formularul de contact sunt prelucrate in conformitate " "cu Regulamentul General privind Protectia Datelor (GDPR). Consultati Politica de " "Confidentialitate pentru detalii complete.", "info", ) add_screenshot(doc, "website", "05-contact.png", "Pagina Contact - Formularul de contact") # 3.6 Confidentialitate doc.add_heading("3.6 Politica de Confidentialitate", level=2) p = doc.add_paragraph() p.add_run( "Pagina de Politica de Confidentialitate contine documentul complet privind protectia " "datelor cu caracter personal, in conformitate cu Regulamentul (UE) 2016/679 (GDPR). " "Documentul acopera urmatoarele aspecte principale:" ) add_bullet_list(doc, [ "Identitatea si datele de contact ale operatorului de date (TOP CLOSSERS SRL)", "Categoriile de date cu caracter personal colectate", "Scopurile si temeiurile juridice ale prelucrarii datelor", "Durata de pastrare a datelor personale", "Drepturile persoanelor vizate (acces, rectificare, stergere, portabilitate, opozitie)", "Informatii privind utilizarea cookie-urilor si tehnologiilor de urmarire", "Masurile de securitate implementate pentru protectia datelor", "Procedura de depunere a plangerilor la Autoritatea Nationala de Supraveghere (ANSPDCP)", ]) add_screenshot(doc, "website", "06-privacy.png", "Pagina Politica de Confidentialitate") doc.add_page_break() # 3.7 Termeni si Conditii doc.add_heading("3.7 Termeni si Conditii", level=2) p = doc.add_paragraph() p.add_run( "Pagina Termeni si Conditii contine acordul legal complet care guverneaza utilizarea " "platformei DiDi. Documentul stabileste drepturile si obligatiile utilizatorilor, " "precum si ale operatorului platformei. Principalele capitole includ:" ) add_bullet_list(doc, [ "Definitii si terminologie utilizata in cadrul acordului", "Conditii de utilizare a platformei si serviciilor", "Procesul de inregistrare si obligatiile utilizatorului privind securitatea contului", "Descrierea serviciilor oferite si limitarile acestora", "Politica de preturi, plati si rambursari", "Proprietatea intelectuala si drepturile de autor", "Limitarea raspunderii operatorului platformei", "Procedura de rezolvare a disputelor", "Legislatia aplicabila si jurisdictia competenta", ]) add_screenshot(doc, "website", "07-terms.png", "Pagina Termeni si Conditii") doc.add_page_break() # ========================================================================= # CHAPTER 4: Autentificare # ========================================================================= doc.add_heading("4. Autentificare si Gestionarea Contului", level=1) p = doc.add_paragraph() p.add_run( "Platforma DiDi utilizeaza sistemul de autentificare Keycloak SSO (Single Sign-On) " "pentru gestionarea securizata a conturilor utilizatorilor. Acest sistem ofera un nivel " "ridicat de securitate si o experienta consistenta de autentificare." ) doc.add_heading("4.1 Crearea Contului (Inregistrare)", level=2) p = doc.add_paragraph() p.add_run( "Pentru a utiliza serviciile platite ale platformei DiDi, utilizatorul trebuie sa isi " "creeze un cont. Procesul de inregistrare se desfasoara in urmatorii pasi:" ) add_numbered_list(doc, [ 'Accesati website-ul DiDi si apasati butonul "Autentificare" sau "Inregistrare" din bara de navigare', "Veti fi redirectionat automat catre pagina de autentificare Keycloak SSO (didi365.eu/didi-auth)", 'Apasati linkul "Inregistrare" de sub formularul de login', "Completati formularul de inregistrare cu urmatoarele informatii: prenume, nume, adresa de email, parola (minim 8 caractere)", "Confirmati adresa de email prin accesarea linkului primit in casuta de email", "Dupa confirmare, contul este activ si puteti accesa dashboard-ul client", ]) add_note_box( doc, "La prima autentificare, sistemul creeaza automat un profil de client in ERPNext " "asociat cu ID-ul unic Keycloak (UUID). Acest profil va fi utilizat pentru facturare, " "istoricul achizitiilor si gestionarea serviciilor.", "info", ) doc.add_heading("4.2 Autentificare (Login)", level=2) p = doc.add_paragraph() p.add_run("Procesul de autentificare se desfasoara astfel:") add_numbered_list(doc, [ 'Apasati butonul "Autentificare" din bara de navigare a website-ului', "Sunteti redirectionat catre pagina de login Keycloak SSO", "Introduceti adresa de email si parola contului dvs.", 'Apasati butonul "Conectare"', "Dupa autentificarea reusita, sunteti redirectionat inapoi pe website cu o sesiune activa", "In bara de navigare va aparea optiunea de acces la Dashboard in locul butonului de login", ]) p = doc.add_paragraph() p.add_run( "Sesiunea de autentificare este gestionata prin JWT (JSON Web Token) cu durata limitata. " "Sesiunea se reinnoieste automat prin mecanismul de refresh token, asigurand o experienta " "fluida fara deconectari frecvente." ) doc.add_heading("4.3 Deconectare (Logout)", level=2) p = doc.add_paragraph() p.add_run( 'Pentru a va deconecta din platforma, apasati butonul "Deconectare" disponibil in ' "bara de navigare sau in meniul de profil din dashboard. La deconectare:" ) add_bullet_list(doc, [ "Sesiunea locala este invalidata", "Token-ul JWT este revocat", "Sunteti redirectionat catre pagina principala a website-ului", "Pentru a accesa din nou dashboard-ul, va trebui sa va autentificati din nou", ]) doc.add_page_break() # ========================================================================= # CHAPTER 5: Dashboard Client # ========================================================================= doc.add_heading("5. Dashboard Client", level=1) p = doc.add_paragraph() p.add_run( "Dashboard-ul client este zona privata a platformei, accesibila doar utilizatorilor autentificati. " "Ofera acces la toate functionalitatile de achizitionare, rulare analize, vizualizare rezultate " "si gestionare a contului." ) # 5.1 Overview doc.add_heading("5.1 Overview (Pagina Principala Dashboard)", level=2) p = doc.add_paragraph() p.add_run( "Pagina Overview este prima pagina afisata dupa autentificare si ofera o vedere de ansamblu " "asupra contului si activitatii utilizatorului. Elementele principale includ:" ) doc.add_heading("Salut Personalizat", level=3) p = doc.add_paragraph() p.add_run( "In partea superioara se afiseaza un mesaj de bun venit personalizat cu numele utilizatorului " 'autentificat (exemplu: "Buna ziua, Vasile!").' ) doc.add_heading("Carduri Statistici", level=3) p = doc.add_paragraph() p.add_run("Dashboard-ul afiseaza carduri informative cu urmatoarele statistici:") add_bullet_list(doc, [ "Achizitii totale - numarul total de servicii de analiza achizitionate", "Disponibile - numarul de analize achizitionate dar inca neutilizate", "Rezumat - prezentare generala a activitatii contului", ]) doc.add_heading("Ultimele Facturi", level=3) p = doc.add_paragraph() p.add_run( "In sectiunea inferioara se afiseaza o lista cu ultimele facturi emise, permitand " "vizualizarea rapida a tranzactiilor recente." ) add_screenshot(doc, "dashboard", "01-overview.png", "Dashboard - Pagina Overview cu statistici si rezumat") doc.add_page_break() # 5.2 Achizitioneaza Analiza doc.add_heading("5.2 Achizitioneaza Analiza", level=2) p = doc.add_paragraph() p.add_run( "Pagina Achizitioneaza Analiza permite utilizatorilor sa cumpere servicii de analiza AI. " "Serviciile sunt organizate pe 4 categorii principale, fiecare disponibila pentru " "diferite tipuri de media." ) doc.add_heading("Categorii de Servicii Disponibile", level=3) add_table_with_header( doc, ["Categorie", "Cod Intern", "Descriere"], [ ["Detectie Tehnici Manipulare", "DIDI-TECHNIQUES-*", "Identificarea tehnicilor de propaganda si manipulare in continut"], ["AI Detection (Deepfake)", "DIDI-AI_DETECTION-*", "Detectarea continutului generat sau modificat de AI"], ["Fact-Checking", "DIDI-CLAIMS-*", "Verificarea automata a afirmatiilor si informatiilor"], ["Evaluare Surse", "DIDI-SOURCE-*", "Analiza credibilitatii domeniilor si surselor de informatii"], ], ) doc.add_heading("Tipuri de Media Acceptate", level=3) add_table_with_header( doc, ["Tip Media", "Format", "Descriere"], [ ["Text", "Text liber", "Continut text introdus direct in platforma"], ["Imagine", "PNG, JPG, WEBP", "Fisiere imagine incarcate de utilizator"], ["Audio", "MP3, WAV, OGG", "Fisiere audio incarcate de utilizator"], ["Video", "MP4, MOV, AVI", "Fisiere video incarcate de utilizator"], ["URL", "Link web", "Adresa URL a continutului de analizat"], ], ) doc.add_heading("Procesul de Achizitie", level=3) p = doc.add_paragraph() p.add_run("Pentru a achizitiona o analiza, urmati pasii:") add_numbered_list(doc, [ "Navigati la sectiunea 'Achizitioneaza Analiza' din meniul lateral al dashboard-ului", "Selectati categoria de analiza dorita (Detectie Tehnici, AI Detection, Fact-Checking, Evaluare Surse)", "Selectati tipul de media (Text, Imagine, Audio, Video, URL)", "Verificati pretul afisat pentru combinatia selectata", 'Apasati butonul "Cumpara" pentru a initia procesul de plata', "Veti fi redirectionat catre pagina de plata Stripe (detalii in Capitolul 6)", ]) add_screenshot(doc, "dashboard", "03-achizitioneaza.png", "Dashboard - Pagina de achizitie servicii analiza") doc.add_page_break() # 5.3 Analize doc.add_heading("5.3 Analize (Lista Analize)", level=2) p = doc.add_paragraph() p.add_run( "Pagina Analize afiseaza toate analizele asociate contului utilizatorului, organizate " "in doua categorii:" ) doc.add_heading("Analize Disponibile (Neconsumate)", level=3) p = doc.add_paragraph() p.add_run( "Analizele achizitionate dar inca neutilizate sunt afisate ca disponibile. Pentru fiecare " "analiza disponibila se afiseaza:" ) add_bullet_list(doc, [ "Tipul analizei (categorie + tip media)", "Data achizitiei", "Numarul facturii asociate", 'Buton "Ruleaza Analiza" pentru a incepe procesul de analiza', ]) doc.add_heading("Rapoarte Salvate", level=3) p = doc.add_paragraph() p.add_run( "Analizele deja efectuate sunt afisate ca rapoarte salvate. Pentru fiecare raport " "se afiseaza:" ) add_bullet_list(doc, [ "Titlul raportului si categoria analizei", "Data efectuarii analizei", "Scorul de risc si nivelul de incredere", 'Buton "Descarca PDF" pentru generarea si descarcarea raportului complet', ]) add_screenshot(doc, "dashboard", "02-analize.png", "Dashboard - Lista analize disponibile si rapoarte salvate") # 5.4 Rulare Analiza doc.add_heading("5.4 Rularea unei Analize", level=2) p = doc.add_paragraph() p.add_run( "Procesul de rulare a unei analize este nucleul functionalitatii platformei DiDi. " "Dupa achizitionarea unui serviciu de analiza, utilizatorul poate rula analiza " "pe continutul dorit." ) doc.add_heading("Pasii pentru Rularea unei Analize", level=3) add_numbered_list(doc, [ "Din pagina Analize, selectati o analiza disponibila (neConsumata) si apasati 'Ruleaza Analiza'", "Se deschide interfata de introducere a continutului, adaptata tipului de media al analizei", "Pentru analize de tip Text: introduceti textul de analizat in campul dedicat", "Pentru analize de tip URL: introduceti adresa web a continutului de analizat", "Pentru analize de tip Imagine/Audio/Video: incarcati fisierul media folosind butonul de upload", 'Apasati butonul "Porneste analiza" pentru a initia procesarea', "Platforma afiseaza un indicator de progres in timp ce analiza este procesata", "Dupa finalizare, rezultatele sunt afisate direct in interfata", ]) doc.add_heading("Structura Rezultatelor", level=3) p = doc.add_paragraph() p.add_run("Rezultatele unei analize includ urmatoarele informatii:") add_table_with_header( doc, ["Element", "Descriere"], [ ["Scor de Risc", "Valoare numerica (0-100%) care indica nivelul de risc de dezinformare detectat"], ["Nivel de Incredere", "Gradul de incredere al algoritmului in rezultatul furnizat"], ["Categorie de Risc", "Clasificare: Scazut / Mediu / Ridicat / Critic"], ["Tehnici Detectate", "Lista tehnicilor de manipulare identificate (pentru categoria Detectie Tehnici)"], ["Verdict", "Concluzia generala a analizei in format text descriptiv"], ["Afirmatii Verificate", "Lista afirmatiilor verificate si statusul lor (pentru Fact-Checking)"], ["Scor AI Tampered", "Probabilitatea ca media sa fie generata/modificata de AI (pentru AI Detection)"], ["Evaluare Domeniu", "Scorul de credibilitate al domeniului analizat (pentru Evaluare Surse)"], ], ) doc.add_heading("Procesare Sincrona vs. Asincrona", level=3) p = doc.add_paragraph() p.add_run( "In functie de tipul de continut, analiza poate fi procesata sincron sau asincron:" ) add_bullet_list(doc, [ "Sincron (rezultat imediat): analize de tip Text, URL si Imagine - rezultatele sunt returnate in cateva secunde", "Asincron (cu polling): analize de tip Audio si Video - procesarea poate dura mai mult, iar platforma verifica periodic statusul analizei (polling) si afiseaza rezultatele cand acestea sunt disponibile", ]) doc.add_heading("Rezultate pentru Diferite Tipuri de Media", level=3) p = doc.add_paragraph() p.add_run( "Mai jos sunt prezentate exemple de rezultate pentru fiecare tip de media analizat:" ) add_screenshot(doc, "dashboard", "08-rezultat-text.png", "Rezultat analiza text - Scor de risc si tehnici detectate") add_screenshot(doc, "dashboard", "09-rezultat-imagine.png", "Rezultat analiza imagine - Detectie AI/manipulare") add_screenshot(doc, "dashboard", "10-rezultat-audio.png", "Rezultat analiza audio - Evaluare continut audio") add_screenshot(doc, "dashboard", "11-rezultat-video.png", "Rezultat analiza video - Detectie deepfake") doc.add_heading("Descarcare Raport PDF", level=3) p = doc.add_paragraph() p.add_run( "Dupa finalizarea analizei, utilizatorul poate descarca un raport complet in format PDF " "care contine toate informatiile analizei, inclusiv detalii tehnice, grafice si recomandari. " 'Raportul se genereaza prin apasarea butonului "Descarca PDF" disponibil in pagina de rezultate.' ) doc.add_page_break() # 5.5 Facturi doc.add_heading("5.5 Facturi", level=2) p = doc.add_paragraph() p.add_run( "Pagina Facturi ofera acces la toate facturile emise pentru achizitiile efectuate. " "Facturile sunt generate automat in sistemul ERPNext la finalizarea fiecarei plati." ) doc.add_heading("Informatii Afisate", level=3) p = doc.add_paragraph() p.add_run("Pentru fiecare factura se afiseaza urmatoarele informatii:") add_table_with_header( doc, ["Camp", "Descriere"], [ ["Numar Factura", "Identificator unic in formatul DIDI-INV-YYYY-##### (exemplu: DIDI-INV-2026-00001)"], ["Data Emitere", "Data la care factura a fost emisa"], ["Suma", "Valoarea totala a facturii (inclusiv TVA 19%)"], ["Status", "Starea facturii: Platita, Anulata"], ["Actiuni", "Buton pentru descarcarea facturii in format PDF"], ], ) p = doc.add_paragraph() p.add_run( "Facturile pot fi filtrate si sortate dupa data sau status. Fiecare factura poate fi " "descarcata in format PDF cu branding DiDi, incluzand toate detaliile legale necesare " "(date companie, CUI, numar de inregistrare, adresa)." ) add_screenshot(doc, "dashboard", "04-facturi.png", "Dashboard - Lista facturi cu status si actiuni") # 5.6 Profil doc.add_heading("5.6 Profil Utilizator", level=2) p = doc.add_paragraph() p.add_run( "Pagina Profil permite utilizatorului sa vizualizeze si sa editeze informatiile " "asociate contului sau. Informatiile disponibile includ:" ) add_bullet_list(doc, [ "Datele personale: nume, prenume, adresa de email (sincronizate din Keycloak)", "Date companie: numele companiei, CUI (pentru facturare persoane juridice)", "Planul activ: tipul de abonament curent (Free Tier, Paid Tier, Enterprise Tier)", "Optiuni GDPR: gestionarea consimtamintelor pentru prelucrarea datelor personale", "Posibilitatea de a solicita exportul sau stergerea datelor personale", ]) p = doc.add_paragraph() p.add_run( "Modificarile la datele de companie si CUI se sincronizeaza automat cu fisa clientului " "din ERPNext, asigurand consistenta datelor de facturare." ) add_screenshot(doc, "dashboard", "05-profil.png", "Dashboard - Pagina de profil utilizator") doc.add_page_break() # ========================================================================= # CHAPTER 6: Plata cu Stripe # ========================================================================= doc.add_heading("6. Plata cu Stripe", level=1) p = doc.add_paragraph() p.add_run( "Platforma DiDi utilizeaza serviciul de plati online Stripe pentru procesarea securizata " "a tranzactiilor. Stripe este un procesator de plati certificat PCI DSS Level 1, " "garantand cel mai inalt nivel de securitate pentru datele cardului." ) doc.add_heading("6.1 Fluxul de Plata", level=2) p = doc.add_paragraph() p.add_run( "Procesul de plata se desfasoara in urmatorii pasi:" ) add_numbered_list(doc, [ "Selectie serviciu: din pagina Achizitioneaza Analiza, selectati serviciul dorit si apasati 'Cumpara'", "Creare sesiune Stripe: website-ul creeaza automat o sesiune de checkout Stripe pe server (API route /api/checkout)", "Redirect la Stripe: sunteti redirectionat catre pagina securizata Stripe Checkout", "Completare date card: introduceti numarul cardului, data expirare, CVV si numele titularului", "Procesare plata: Stripe proceseaza plata si valideaza tranzactia", "Redirect success: dupa plata reusita, sunteti redirectionat inapoi pe website la pagina de confirmare", "Confirmare: se afiseaza referinta platii si numarul facturii generate automat in ERPNext", ]) doc.add_heading("6.2 Procesare Automata (Webhook Fulfillment)", level=2) p = doc.add_paragraph() p.add_run( "La confirmarea platii, Stripe trimite automat o notificare (webhook) catre platforma DiDi. " "Aceasta notificare declanseaza urmatorul proces automat:" ) add_bullet_list(doc, [ "Verificarea si validarea platii prin semnatura webhook-ului Stripe", "Crearea sau identificarea clientului in ERPNext (pe baza ID-ului Keycloak)", "Generarea automata a facturii (Sales Invoice) cu seria DIDI-INV-YYYY-#####", "Inregistrarea automata a platii (Payment Entry) in contabilitatea ERPNext", "Crearea acordului de servicii (Service Agreement) care activeaza analiza achizitionata", "Actualizarea planului clientului daca este cazul", "Inregistrarea tranzactiei in jurnalul de plati (Payment Log) pentru audit", "Trimiterea automata a facturii prin email catre client", ]) doc.add_heading("6.3 Carduri Acceptate", level=2) p = doc.add_paragraph() p.add_run( "Stripe accepta urmatoarele metode de plata: Visa, Mastercard, American Express, " "Discover si alte carduri bancare internationale. De asemenea, sunt suportate " "portofelele digitale Apple Pay si Google Pay (in functie de browser si dispozitiv)." ) add_note_box( doc, "Datele cardului dvs. sunt procesate exclusiv de Stripe si nu sunt niciodata " "stocate pe serverele platformei DiDi. Platforma nu are acces la numarul " "complet al cardului sau la codul CVV.", "info", ) doc.add_page_break() # ========================================================================= # CHAPTER 7: Design Responsive # ========================================================================= doc.add_heading("7. Design Responsive", level=1) p = doc.add_paragraph() p.add_run( "Website-ul DiDi este dezvoltat cu un design responsive care se adapteaza automat " "la dimensiunea ecranului dispozitivului utilizat. Interfata a fost optimizata " "pentru trei categorii principale de dispozitive:" ) doc.add_heading("7.1 Desktop (> 1024px)", level=2) p = doc.add_paragraph() p.add_run( "Pe ecranele de desktop, website-ul afiseaza continutul la latimea completa, " "cu bara de navigare orizontala vizibila permanent, layout pe coloane multiple " "si imagini la rezolutie completa. Aceasta este experienta optima recomanda." ) doc.add_heading("7.2 Tableta (768px - 1024px)", level=2) p = doc.add_paragraph() p.add_run( "Pe tablete, layout-ul se reorganizeaza pentru a se adapta la ecranul mai mic. " "Coloanele se reduc, iar unele elemente se stivuiesc vertical. Bara de navigare " "poate trece in modul hamburger menu." ) add_screenshot(doc, "website", "09-homepage-tablet.png", "Website - Vizualizare pe tableta") doc.add_heading("7.3 Mobil (< 768px)", level=2) p = doc.add_paragraph() p.add_run( "Pe dispozitive mobile, interfata se optimizeaza complet: navbar-ul devine un " "meniu hamburger, continutul se afiseaza pe o singura coloana, butoanele si " "elementele interactive sunt dimensionate pentru interactiunea tactila, iar " "textul se redimensioneaza pentru lizibilitate optima." ) add_screenshot(doc, "website", "08-homepage-mobile.png", "Website - Vizualizare pe mobil (smartphone)") add_screenshot(doc, "dashboard", "06-overview-mobile.png", "Dashboard - Vizualizare pe mobil") doc.add_page_break() # ========================================================================= # CHAPTER 8: Functionalitati Suplimentare # ========================================================================= doc.add_heading("8. Functionalitati Suplimentare", level=1) doc.add_heading("8.1 Cookie Consent (Consimtamant Cookie-uri)", level=2) p = doc.add_paragraph() p.add_run( "La prima vizita pe website, utilizatorului i se prezinta un banner de consimtamant " "pentru cookie-uri, in conformitate cu legislatia europeana. Utilizatorul poate " "gestiona urmatoarele categorii de cookie-uri:" ) add_table_with_header( doc, ["Categorie", "Obligatoriu", "Descriere"], [ ["Necesare", "Da", "Cookie-uri esentiale pentru functionarea website-ului (sesiune, securitate)"], ["Functionale", "Nu", "Cookie-uri care imbunatatesc experienta (preferinte limba, tema)"], ["Analitice", "Nu", "Cookie-uri pentru colectarea datelor de utilizare si performanta"], ], ) p = doc.add_paragraph() p.add_run( "Cookie-urile necesare nu pot fi dezactivate deoarece sunt esentiale pentru " "functionarea corecta a website-ului. Cookie-urile functionale si analitice " "pot fi activate sau dezactivate individual de catre utilizator." ) doc.add_heading("8.2 Internationalizare (i18n) - RO/EN", level=2) p = doc.add_paragraph() p.add_run( "Website-ul suporta doua limbi: Romana (RO) si Engleza (EN). Continutul tuturor " "paginilor publice este disponibil in ambele limbi, inclusiv: texte interfata, " "etichete butoane, mesaje de eroare si notificari, continut pagini (gestionat " "prin CMS ERPNext). Preferinta de limba se salveaza in browserul utilizatorului " "(localStorage) si este pastrata la vizitele ulterioare." ) doc.add_heading("8.3 Sitemap.xml si SEO", level=2) p = doc.add_paragraph() p.add_run( "Website-ul genereaza automat un fisier sitemap.xml care contine toate paginile " "publice ale website-ului. Acest fisier este utilizat de motoarele de cautare " "(Google, Bing) pentru indexarea corecta a continutului." ) p = doc.add_paragraph() p.add_run("Optimizarile SEO implementate includ:") add_bullet_list(doc, [ "Metadata (title, description) unice pentru fiecare pagina", "Tag-uri Open Graph pentru partajarea pe retele sociale", "Structura semantica HTML5 corecta (headings, sections, articles)", "Fisier robots.txt configurat corect", "URL-uri curate si descriptive", "Performanta optimizata (Lighthouse SEO score: 100/100)", ]) doc.add_page_break() # ========================================================================= # CHAPTER 9: Depanare si Contact # ========================================================================= doc.add_heading("9. Depanare si Contact Suport", level=1) doc.add_heading("9.1 Probleme Frecvente", level=2) add_table_with_header( doc, ["Problema", "Cauza Posibila", "Solutie"], [ ["Nu pot accesa website-ul", "Conexiune internet instabila sau URL incorect", "Verificati conexiunea si accesati https://didi365.eu"], ["Nu ma pot autentifica", "Credentiale incorecte sau cont neconfirmat", "Verificati email-ul de confirmare sau resetati parola"], ["Plata a esuat", "Card expirat, fonduri insuficiente sau eroare Stripe", "Verificati datele cardului sau incercati alt card"], ["Analiza nu porneste", "Nu exista analize disponibile (neachizitionate)", "Achizitionati o analiza noua din pagina dedicata"], ["Rezultatele intarzie", "Analiza asincrona (audio/video) in procesare", "Asteptati finalizarea - platforma verifica automat statusul"], ["Nu pot descarca factura", "Eroare temporara de conectare la ERPNext", "Reincarcati pagina sau incercati mai tarziu"], ["Website-ul se afiseaza incorect", "Browser vechi sau cache corupt", "Actualizati browserul si stergeti cache-ul"], ], ) doc.add_heading("9.2 Contact Suport Tehnic", level=2) p = doc.add_paragraph() p.add_run( "Pentru asistenta suplimentara sau probleme nerezolvate, puteti contacta " "echipa de suport prin urmatoarele canale:" ) add_table_with_header( doc, ["Canal", "Detalii"], [ ["Email", "office@clossers.com"], ["Formular Contact", "Accesibil pe pagina Contact a website-ului"], ["Program Suport", "Luni - Vineri, 09:00 - 18:00"], ], ) p = doc.add_paragraph() p.add_run( "Cand contactati echipa de suport, va rugam sa includeti urmatoarele informatii " "pentru a facilita rezolvarea rapida a problemei:" ) add_bullet_list(doc, [ "Adresa de email asociata contului DiDi", "Descrierea detaliata a problemei intampinate", "Pasii parcursi inainte de aparitia problemei", "Capturi de ecran relevante (daca este posibil)", "Browserul si versiunea utilizata", "Dispozitivul folosit (desktop, tableta, mobil)", ]) # Save document output_path = os.path.join(OUTPUT_DIR, "Manual_Utilizare_Website.docx") doc.save(output_path) print(f" -> Salvat: {output_path}") return output_path # ============================================================================== # DOCUMENT 2: Ghid Administrare ERPNext # ============================================================================== def generate_ghid_erpnext(): """Generate Ghid_Administrare_ERPNext.docx""" print("[2/4] Generare Ghid_Administrare_ERPNext.docx ...") doc = create_document() add_cover_page( doc, "GHID DE ADMINISTRARE", "ERPNext ERP & CRM", ) add_header_footer(doc) add_toc_placeholder(doc) # ========================================================================= # CHAPTER 1: Introducere # ========================================================================= doc.add_heading("1. Introducere", level=1) doc.add_heading("1.1 Scopul Documentului", level=2) p = doc.add_paragraph() p.add_run( "Acest ghid de administrare ofera instructiuni detaliate pentru utilizarea si " "gestionarea sistemului ERPNext ERP & CRM integrat in platforma DiDi. Documentul " "se adreseaza administratorilor de sistem si personalului responsabil cu operatiunile " "de back-office ale platformei." ) doc.add_heading("1.2 Despre ERPNext", level=2) p = doc.add_paragraph() p.add_run( "ERPNext este o solutie open-source de tip Enterprise Resource Planning (ERP) si " "Customer Relationship Management (CRM), dezvoltata pe framework-ul Frappe. " "Platforma DiDi utilizeaza ERPNext versiunea 15, configurata si personalizata " "pentru nevoile specifice ale proiectului." ) p = doc.add_paragraph() p.add_run("ERPNext ofera urmatoarele module integrate utilizate in cadrul platformei DiDi:") add_bullet_list(doc, [ "Facturare (Sales Invoice) - generarea si gestionarea facturilor fiscale", "CRM (Lead Management) - gestionarea lead-urilor si pipeline-ului de vanzari", "Gestiune Clienti (Customer) - evidenta clientilor cu date personalizate", "Plati (Payment Entry, Payment Log) - inregistrarea si urmarirea platilor", "Rapoarte Analiza (Analysis Report) - stocarea rezultatelor analizelor AI", "Servicii (Item, Service Agreement) - catalogul de servicii si acordurile active", "CMS (Website Content) - gestionarea continutului website-ului bilingv", "Notificari Email - comunicare automata cu clientii", "Contabilitate - plan de conturi Romania cu TVA 19%", ]) doc.add_heading("1.3 Deployment Docker", level=2) p = doc.add_paragraph() p.add_run( "ERPNext este rulat intr-un mediu containerizat Docker, asigurand izolarea " "serviciilor, reproducibilitate si usurinta in administrare. Stiva de containere " "include: ERPNext web, MariaDB (baza de date), Redis Cache, Redis Queue, " "3 workeri pentru procesare asincrona si un scheduler pentru taskuri planificate." ) doc.add_heading("1.4 URL de Acces", level=2) add_code_block(doc, "http://localhost:8080") add_note_box( doc, "Accesul la interfata de administrare ERPNext este disponibil exclusiv din " "reteaua interna sau prin tunel securizat. Nu expuneti portul 8080 direct " "pe internet fara masuri de securitate suplimentare (firewall, VPN).", "warning", ) doc.add_page_break() # ========================================================================= # CHAPTER 2: Acces si Autentificare # ========================================================================= doc.add_heading("2. Acces si Autentificare", level=1) doc.add_heading("2.1 Autentificare Administrator", level=2) p = doc.add_paragraph() p.add_run( "Pentru a accesa interfata de administrare ERPNext, navigati la adresa de acces " "si utilizati credentialele de administrator:" ) add_table_with_header( doc, ["Camp", "Valoare"], [ ["URL", "http://localhost:8080"], ["Utilizator", "Administrator"], ["Parola", "admin (de schimbat la prima utilizare)"], ], ) add_note_box( doc, "La prima accesare, este obligatorie schimbarea parolei implicite 'admin' " "cu o parola complexa (minim 12 caractere, litere mari si mici, cifre, " "caractere speciale).", "warning", ) doc.add_heading("2.2 Interfata in Limba Romana", level=2) p = doc.add_paragraph() p.add_run( "ERPNext a fost configurat cu limba romana ca limba principala a interfetei. " "Toate meniurile, etichetele si mesajele de sistem sunt afisate in limba romana. " "Limba poate fi modificata din setarile profilului utilizatorului daca este necesar." ) doc.add_heading("2.3 Navigarea in Interfata", level=2) p = doc.add_paragraph() p.add_run( "Interfata ERPNext este organizata pe baza de Workspace-uri (spatii de lucru). " "Fiecare workspace grupeaza functionalitatile relevante pentru un domeniu specific. " "Navigarea se face prin:" ) add_bullet_list(doc, [ "Sidebar (bara laterala stanga) - acces rapid la workspace-uri si module", "Awesome Bar (bara de cautare) - cautare rapida documente, rapoarte, pagini (Ctrl+K)", "Breadcrumbs - navigare ierarhica in structura de documente", "Shortcuts (scurtaturi) - acces rapid la documentele frecvent utilizate", ]) doc.add_page_break() # ========================================================================= # CHAPTER 3: Workspace DiDi Platform # ========================================================================= doc.add_heading("3. Workspace DiDi Platform", level=1) p = doc.add_paragraph() p.add_run( "Workspace-ul DiDi Platform este spatiul de lucru personalizat care centralizeaza " "toate functionalitatile specifice platformei DiDi. Acesta ofera scurtaturi rapide " "catre cele mai utilizate documente si rapoarte." ) doc.add_heading("3.1 Scurtaturi Disponibile", level=2) add_table_with_header( doc, ["Scurtatura", "DocType / Modul", "Descriere"], [ ["Facturi", "Sales Invoice", "Lista si gestionarea facturilor emise"], ["Clienti", "Customer", "Evidenta clientilor platformei"], ["CRM", "Lead", "Gestionarea lead-urilor si pipeline vanzari"], ["Servicii", "Item", "Catalogul de servicii DiDi"], ["Plati", "Payment Log", "Jurnalul de plati Stripe"], ["Contabilitate", "Account", "Plan de conturi si rapoarte financiare"], ["Website CMS", "Website Content", "Gestionarea continutului website-ului"], ["Rapoarte Analiza", "Analysis Report", "Rezultatele analizelor AI efectuate"], ["Acorduri Servicii", "Service Agreement", "Acorduri active de prestare servicii"], ["Incasari", "Payment Entry", "Inregistrari plati in contabilitate"], ["Notificari", "Notification", "Configurare notificari email automate"], ], ) add_screenshot(doc, "erpnext", "01-workspace.png", "Workspace DiDi Platform - Panou central de administrare") doc.add_page_break() # ========================================================================= # CHAPTER 4: Gestiune Facturi # ========================================================================= doc.add_heading("4. Gestiune Facturi (Facturare)", level=1) p = doc.add_paragraph() p.add_run( "Modulul de facturare gestioneaza toate facturile emise prin platforma DiDi. " "Facturile sunt generate automat la finalizarea platilor prin Stripe si " "urmeaza seria de numerotare DIDI-INV-YYYY-#####." ) doc.add_heading("4.1 Lista Facturi", level=2) p = doc.add_paragraph() p.add_run( "Lista facturilor afiseaza toate facturile emise in sistem. Interfata permite:" ) add_bullet_list(doc, [ "Filtrare dupa status (Platita, Anulata, Ciorna), client, data, suma", "Sortare dupa orice coloana (numar, data, client, suma, status)", "Cautare rapida dupa numarul facturii sau numele clientului", "Export lista in format CSV sau Excel", "Creare manuala de facturi noi (pentru cazuri speciale)", ]) doc.add_heading("Statusuri Factura", level=3) add_table_with_header( doc, ["Status", "Cod Culoare", "Descriere"], [ ["Ciorna (Draft)", "Gri", "Factura creata dar nevalidata - poate fi editata"], ["Trimisa (Submitted)", "Albastru", "Factura validata si trimisa clientului"], ["Platita (Paid)", "Verde", "Factura a fost achitata integral"], ["Anulata (Cancelled)", "Rosu", "Factura a fost anulata (storno)"], ], ) add_screenshot(doc, "erpnext", "02-facturi.png", "Lista facturi - Vizualizare si filtrare") doc.add_heading("4.2 Detaliu Factura", level=2) p = doc.add_paragraph() p.add_run( "Pagina de detaliu a unei facturi contine toate informatiile asociate tranzactiei:" ) add_bullet_list(doc, [ "Date client: nume, adresa, CUI (pentru persoane juridice)", "Articole facturate: serviciul DiDi achizitionat, cantitate, pret unitar", "TVA 19%: calculat automat conform legislatiei fiscale romanesti", "Totaluri: subtotal, TVA, total de plata", "Referinte: numar factura, seria, data emitere, data scadenta", "Camp custom 'Analiza Consumata': indica daca analiza asociata a fost utilizata", "Camp custom 'Session ID': identificatorul sesiunii de analiza AI (daca a fost rulata)", "Istoric: timeline cu toate actiunile efectuate asupra facturii", ]) add_screenshot(doc, "erpnext", "03-factura-detaliu.png", "Detaliu factura - Informatii complete despre tranzactie") doc.add_heading("4.3 Print Format DiDi Invoice", level=2) p = doc.add_paragraph() p.add_run( "Facturile DiDi utilizeaza un format de tiparire (Print Format) personalizat " "care include branding-ul platformei si toate informatiile legale obligatorii:" ) add_bullet_list(doc, [ "Logo si branding DiDi / TOP CLOSSERS SRL", "Date complete emitent: nume companie, CUI, numar registru comert, adresa, cont bancar", "Date complete beneficiar: nume, CUI (daca exista), adresa", "Tabel articole cu descriere detaliata, cantitate, pret unitar, valoare", "Totaluri: subtotal, TVA 19%, total de plata", "Seria si numarul facturii, data emitere", "Mentiuni legale obligatorii conform legislatiei fiscale romanesti", ]) add_screenshot(doc, "erpnext", "13-print-format-factura.png", "Print Format - Factura PDF cu branding DiDi") doc.add_heading("4.4 Serie Facturi", level=2) p = doc.add_paragraph() p.add_run( "Toate facturile emise in platforma DiDi utilizeaza seria de numerotare " "DIDI-INV-YYYY-##### unde YYYY este anul curent si ##### este numarul " "secvential (cu 5 cifre, completat cu zero-uri). Exemplu: DIDI-INV-2026-00042." ) doc.add_page_break() # ========================================================================= # CHAPTER 5: Gestiune Clienti # ========================================================================= doc.add_heading("5. Gestiune Clienti", level=1) doc.add_heading("5.1 Fisa Client", level=2) p = doc.add_paragraph() p.add_run( "Fiecare client al platformei DiDi are o fisa detaliata in ERPNext care contine " "atat informatiile standard ale unui client ERPNext cat si campuri personalizate " "specifice platformei." ) doc.add_heading("Campuri Standard", level=3) add_table_with_header( doc, ["Camp", "Descriere"], [ ["Customer Name", "Numele complet al clientului"], ["Customer Type", "Tip client: Individual sau Company"], ["Customer Group", "Grupul de clienti (ex: Individual, Corporate)"], ["Territory", "Teritoriul geografic (ex: Romania)"], ["Email", "Adresa de email principala"], ["Mobile No", "Numar de telefon mobil"], ], ) doc.add_heading("5.2 Campuri Personalizate (Custom Fields)", level=2) add_table_with_header( doc, ["Camp", "Fieldname", "Tip", "Descriere"], [ ["ID Utilizator DiDi", "didi_user_id", "Data", "UUID Keycloak - legatura unica cu contul de autentificare"], ["Rol IAM", "iam_role", "Select", "Rol acces: free_tier / paid_tier / enterprise_tier"], ["Plan Activ", "active_plan", "Data", "Planul de abonament activ al clientului"], ], ) p = doc.add_paragraph() p.add_run( "Campul didi_user_id este esential pentru integrarea cu sistemul de autentificare " "Keycloak. Acest camp stocheaza UUID-ul unic al utilizatorului din Keycloak si este " "folosit de website pentru a identifica clientul ERPNext asociat sesiunii autentificate." ) add_screenshot(doc, "erpnext", "04-client.png", "Fisa client - Campuri standard si personalizate") doc.add_page_break() # ========================================================================= # CHAPTER 6: CRM - Gestiune Lead-uri # ========================================================================= doc.add_heading("6. CRM - Gestiune Lead-uri", level=1) p = doc.add_paragraph() p.add_run( "Modulul CRM din ERPNext gestioneaza pipeline-ul de vanzari al platformei DiDi, " "de la captarea lead-urilor pana la conversia acestora in clienti activi." ) doc.add_heading("6.1 Pipeline Vanzari", level=2) p = doc.add_paragraph() p.add_run( "Lead-urile parcurg urmatorul pipeline de vanzari in cadrul platformei DiDi:" ) add_table_with_header( doc, ["Etapa", "Status", "Descriere", "Actiune Urmatoare"], [ ["1. Lead", "Open", "Lead nou capturat prin formular contact sau pagina pricing", "Calificare"], ["2. Calificat", "Replied", "Lead calificat de echipa de vanzari", "Programare demo"], ["3. Demo", "Opportunity", "Demo programat sau efectuat cu potentialul client", "Conversie"], ["4. Client", "Converted", "Lead convertit in client activ cu cont pe platforma", "Finalizat"], ], ) doc.add_heading("6.2 Surse Lead-uri (Lead Sources)", level=2) p = doc.add_paragraph() p.add_run( "Platforma DiDi capteaza lead-uri din urmatoarele surse, fiecare configurata " "in ERPNext pentru tracking precis:" ) add_table_with_header( doc, ["Sursa", "Descriere", "Mecanism Captura"], [ ["Website Contact Form", "Formularul de contact de pe pagina Contact", "API POST /api/leads automat"], ["Pricing Page", "Vizitatori care interactioneaza cu pagina de preturi", "Tracking UTM + formular"], ["Demo Request", "Solicitari directe de demonstratie a platformei", "Email sau formular dedicat"], ], ) doc.add_heading("6.3 UTM Tracking", level=2) p = doc.add_paragraph() p.add_run( "Toate lead-urile captate prin website includ parametri UTM pentru identificarea " "sursei de trafic. Parametrii UTM stocati in fisa lead-ului sunt:" ) add_bullet_list(doc, [ "UTM Source - sursa traficului (ex: google, facebook, newsletter)", "UTM Medium - mediul de marketing (ex: cpc, organic, email, social)", "UTM Campaign - campania specifica (ex: launch_2026, pnrr_awareness)", ]) add_screenshot(doc, "erpnext", "05-leaduri-crm.png", "CRM - Lista lead-uri si pipeline vanzari") doc.add_page_break() # ========================================================================= # CHAPTER 7: Plati si Incasari # ========================================================================= doc.add_heading("7. Plati si Incasari", level=1) doc.add_heading("7.1 Payment Log (Jurnal Plati)", level=2) p = doc.add_paragraph() p.add_run( "Payment Log este un DocType custom dezvoltat special pentru platforma DiDi, care " "inregistreaza un audit trail complet al tuturor tranzactiilor Stripe. Fiecare " "intrare in jurnal contine:" ) add_table_with_header( doc, ["Camp", "Descriere"], [ ["Customer", "Referinta catre clientul ERPNext asociat platii"], ["Event Type", "Tipul evenimentului Stripe (ex: checkout.session.completed)"], ["Status", "Statusul tranzactiei (Success, Failed, Pending)"], ["Amount", "Suma platita (in RON)"], ["Stripe Payment ID", "Identificatorul unic al platii in Stripe (pi_xxx)"], ["Stripe Session ID", "Identificatorul sesiunii de checkout Stripe (cs_xxx)"], ["Invoice Reference", "Referinta catre factura ERPNext generata"], ["Timestamp", "Data si ora exacta a tranzactiei"], ["Raw Webhook Data", "Datele brute primite de la Stripe (JSON) pentru audit complet"], ], ) add_screenshot(doc, "erpnext", "06-payment-logs.png", "Payment Log - Jurnalul de plati Stripe") doc.add_heading("7.2 Payment Entry (Incasari)", level=2) p = doc.add_paragraph() p.add_run( "Payment Entry este documentul standard ERPNext pentru inregistrarea incasarilor " "in contabilitate. Fiecare plata Stripe reusita genereaza automat un Payment Entry " "care reconciliaza factura emisa cu suma incasata." ) p = doc.add_paragraph() p.add_run("Informatii stocate in Payment Entry:") add_bullet_list(doc, [ "Tipul platii: Receive (incasare)", "Modul de plata: Stripe (plata online)", "Cont incasari: contul bancar configurat pentru Stripe", "Referinta: legatura cu factura (Sales Invoice) corespunzatoare", "Suma: valoarea incasata in RON", "Data platii: data la care a fost procesata plata de Stripe", ]) add_screenshot(doc, "erpnext", "11-plati-incasari.png", "Payment Entry - Incasari inregistrate automat") doc.add_page_break() # ========================================================================= # CHAPTER 8: Rapoarte Analiza # ========================================================================= doc.add_heading("8. Rapoarte Analiza", level=1) p = doc.add_paragraph() p.add_run( "Analysis Report este un DocType custom care stocheaza rezultatele complete ale " "analizelor AI efectuate prin platforma DiDi." ) doc.add_heading("8.1 Lista Rapoarte", level=2) p = doc.add_paragraph() p.add_run("Fiecare raport de analiza contine urmatoarele informatii:") add_table_with_header( doc, ["Camp", "Descriere"], [ ["Titlu", "Titlul descriptiv al raportului de analiza"], ["Client", "Referinta catre clientul care a solicitat analiza"], ["Factura", "Referinta catre factura asociata achizitiei"], ["Status", "Statusul raportului (Finalizat, In Procesare, Eroare)"], ["Session ID", "Identificatorul sesiunii de analiza din DiDi API"], ["Tip Analiza", "Categoria analizei (Tehnici, AI Detection, Claims, Source)"], ["Tip Media", "Tipul de continut analizat (Text, Imagine, Audio, Video, URL)"], ["Data Creare", "Data si ora la care a fost generat raportul"], ], ) add_screenshot(doc, "erpnext", "07-rapoarte-analiza.png", "Lista rapoarte analiza - Vizualizare si filtrare") doc.add_heading("8.2 Detaliu Raport", level=2) p = doc.add_paragraph() p.add_run( "Pagina de detaliu a unui raport de analiza contine informatiile complete " "ale rezultatului, inclusiv:" ) add_bullet_list(doc, [ "Result JSON - rezultatul complet al analizei in format JSON structurat, incluzand: scor de risc, nivel de incredere, categorie de risc, tehnici detectate, verdict, afirmatii verificate", "PDF Atasat - raportul generat in format PDF care poate fi descarcat sau trimis clientului", "Metadate - informatii despre procesarea analizei: durata, timestamp-uri, versiunea API", "Legatura cu factura si acordul de servicii asociat", ]) add_screenshot(doc, "erpnext", "14-raport-analiza-detaliu.png", "Detaliu raport analiza - Rezultat JSON si PDF") doc.add_page_break() # ========================================================================= # CHAPTER 9: Servicii si Produse # ========================================================================= doc.add_heading("9. Servicii si Produse", level=1) doc.add_heading("9.1 Items DiDi (Catalogul de Servicii)", level=2) p = doc.add_paragraph() p.add_run( "Catalogul de servicii DiDi contine 17 articole (Items) care corespund tuturor " "combinatiilor de categorii de analiza si tipuri de media disponibile. " "Fiecare articol are un cod unic, un pret stabilit si este asociat cu un " "price ID Stripe pentru procesarea platilor." ) doc.add_heading("Lista Completa Servicii", level=3) add_table_with_header( doc, ["Cod Articol", "Categorie", "Tip Media", "Pret (RON)"], [ ["DIDI-TECHNIQUES-TEXT", "Detectie Tehnici", "Text", "9,99"], ["DIDI-TECHNIQUES-IMAGE", "Detectie Tehnici", "Imagine", "14,99"], ["DIDI-TECHNIQUES-AUDIO", "Detectie Tehnici", "Audio", "19,99"], ["DIDI-TECHNIQUES-VIDEO", "Detectie Tehnici", "Video", "24,99"], ["DIDI-AI_DETECTION-TEXT", "AI Detection", "Text", "9,99"], ["DIDI-AI_DETECTION-IMAGE", "AI Detection", "Imagine", "14,99"], ["DIDI-AI_DETECTION-AUDIO", "AI Detection", "Audio", "19,99"], ["DIDI-AI_DETECTION-VIDEO", "AI Detection", "Video", "24,99"], ["DIDI-CLAIMS-TEXT", "Fact-Checking", "Text", "9,99"], ["DIDI-CLAIMS-IMAGE", "Fact-Checking", "Imagine", "14,99"], ["DIDI-CLAIMS-AUDIO", "Fact-Checking", "Audio", "19,99"], ["DIDI-CLAIMS-VIDEO", "Fact-Checking", "Video", "24,99"], ["DIDI-SOURCE-TEXT", "Evaluare Surse", "Text", "9,99"], ["DIDI-SOURCE-IMAGE", "Evaluare Surse", "Imagine", "14,99"], ["DIDI-SOURCE-AUDIO", "Evaluare Surse", "Audio", "19,99"], ["DIDI-SOURCE-VIDEO", "Evaluare Surse", "Video", "24,99"], ["DIDI-SOURCE-URL", "Evaluare Surse", "URL", "9,99"], ], ) add_screenshot(doc, "erpnext", "08-servicii-didi.png", "Lista servicii DiDi - Catalogul complet de articole") doc.add_heading("9.2 Acorduri Servicii (Service Agreement)", level=2) p = doc.add_paragraph() p.add_run( "Service Agreement este un DocType custom care inregistreaza dreptul clientului " "de a utiliza un serviciu de analiza achizitionat. Fiecare acord contine:" ) add_bullet_list(doc, [ "Referinta client - clientul beneficiar al acordului", "Serviciu - articolul DiDi achizitionat (ex: DIDI-TECHNIQUES-TEXT)", "Factura de origine - factura care a generat acordul", "Status - Activ (neutilizat) / Consumat (analiza efectuata) / Expirat", "Data creare - momentul activarii acordului (la confirmarea platii)", "Data expirare - data limita de utilizare (daca aplicabil)", ]) p = doc.add_paragraph() p.add_run( "Cand un client ruleaza o analiza, acordul de servicii corespunzator este marcat " "automat ca 'Consumat', iar factura asociata primeste referinta la sesiunea de " "analiza (analysis_session_id) si flagul analysis_consumed." ) add_screenshot(doc, "erpnext", "09-acorduri-servicii.png", "Acorduri servicii - Evidenta drepturilor de utilizare") doc.add_page_break() # ========================================================================= # CHAPTER 10: CMS - Continut Website # ========================================================================= doc.add_heading("10. CMS - Continut Website", level=1) doc.add_heading("10.1 Website Content", level=2) p = doc.add_paragraph() p.add_run( "Website Content este un DocType custom care permite gestionarea continutului " "paginilor website-ului DiDi direct din interfata de administrare ERPNext. " "Acest mecanism CMS elimina necesitatea modificarii codului sursa pentru " "actualizarea continutului." ) p = doc.add_paragraph() p.add_run("Paginile gestionate prin CMS:") add_table_with_header( doc, ["Pagina", "Slug", "Descriere"], [ ["Homepage", "homepage", "Continutul paginii principale (hero, features, how it works)"], ["Servicii", "services", "Descrierile detaliate ale celor 6 module de analiza"], ["Despre", "about", "Informatii despre companie, echipa si viziune"], ["Pricing", "pricing", "Descrieri categorii si mesaje comerciale"], ["Contact", "contact", "Informatii de contact si text introductiv formular"], ["Privacy", "privacy", "Politica de confidentialitate GDPR completa"], ["Terms", "terms", "Termenii si conditiile de utilizare complete"], ], ) doc.add_heading("Continut Bilingv (RO/EN)", level=3) p = doc.add_paragraph() p.add_run( "Fiecare intrare Website Content contine campuri separate pentru limba romana " "si limba engleza. Atunci cand website-ul solicita continutul unei pagini, " "API-ul ERPNext returneaza textul in limba solicitata de utilizator. " "Modificarile de continut se fac direct din ERPNext fara deployment nou." ) add_screenshot(doc, "erpnext", "10-cms-content.png", "CMS Website Content - Gestionare continut bilingv") doc.add_page_break() # ========================================================================= # CHAPTER 11: Notificari Email # ========================================================================= doc.add_heading("11. Notificari Email", level=1) doc.add_heading("11.1 Email-uri Automate Configurate", level=2) p = doc.add_paragraph() p.add_run( "Platforma DiDi are configurate urmatoarele notificari email automate care " "se trimit in mod automat la producerea evenimentelor asociate:" ) add_table_with_header( doc, ["Notificare", "Eveniment Declansator", "Destinatar", "Continut"], [ ["Factura Emisa", "Creare Sales Invoice (Submit)", "Client (email)", "Factura in format PDF atasata, detalii tranzactie"], ["Plata Confirmata", "Payment Entry creat cu succes", "Client (email)", "Confirmare plata, referinta tranzactie, acces serviciu"], ["Expirare Abonament", "Service Agreement aproape de expirare", "Client (email)", "Avertisment expirare, link reinnoire"], ], ) add_screenshot(doc, "erpnext", "12-notificari-email.png", "Configurare notificari email automate") doc.add_heading("11.2 Email Account - DiDi Outgoing", level=2) p = doc.add_paragraph() p.add_run( "Trimiterea email-urilor se face prin contul de email configurat cu SendGrid SMTP. " "Configurarea include:" ) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Provider", "SendGrid"], ["Protocol", "SMTP (TLS)"], ["Server", "smtp.sendgrid.net"], ["Port", "587"], ["Autentificare", "API Key SendGrid"], ["Email Expeditor", "noreply@didi365.eu"], ["Nume Expeditor", "DiDi Platform"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 12: Contabilitate # ========================================================================= doc.add_heading("12. Contabilitate", level=1) doc.add_heading("12.1 Plan de Conturi Romania", level=2) p = doc.add_paragraph() p.add_run( "ERPNext a fost configurat cu planul de conturi conform reglementarilor contabile " "din Romania. Principalele conturi utilizate in operatiunile platformei DiDi includ:" ) add_table_with_header( doc, ["Cont", "Tip", "Descriere"], [ ["Venituri din Servicii", "Venit", "Venituri din prestarea serviciilor de analiza DiDi"], ["TVA Colectat", "Pasiv", "TVA 19% colectat la facturarea serviciilor"], ["Cont Stripe", "Activ (Banca)", "Incasari din platile Stripe"], ["Cheltuieli Hosting", "Cheltuiala", "Costuri server Hetzner"], ["Cheltuieli Software", "Cheltuiala", "Licente si abonamente software"], ["Cheltuieli Marketing", "Cheltuiala", "Costuri campanii marketing"], ["Comisioane Stripe", "Cheltuiala", "Comisioanele procesatorului de plati Stripe"], ], ) doc.add_heading("12.2 TVA 19%", level=2) p = doc.add_paragraph() p.add_run( "Toate facturile emise prin platforma DiDi includ TVA 19% conform legislatiei " "fiscale romanesti. TVA-ul este calculat automat de ERPNext pe baza template-ului " "de taxe configurat. Preturile afisate pe website includ TVA." ) doc.add_heading("12.3 Furnizori", level=2) p = doc.add_paragraph() p.add_run( "In contabilitatea ERPNext sunt configurati urmatorii furnizori principali " "ai platformei:" ) add_table_with_header( doc, ["Furnizor", "Serviciu", "Tip Cheltuiala"], [ ["Hetzner Online GmbH", "Hosting servere (VPS, Cloud)", "Cheltuieli Hosting"], ["Stripe, Inc.", "Procesare plati online", "Comisioane Stripe"], ["SendGrid (Twilio)", "Serviciu email transactional SMTP", "Cheltuieli Software"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 13: Docker si Mentenanta # ========================================================================= doc.add_heading("13. Docker si Mentenanta", level=1) doc.add_heading("13.1 Pornirea Serviciilor", level=2) p = doc.add_paragraph() p.add_run( "ERPNext ruleaza intr-un mediu Docker Compose. Pentru pornirea intregului " "stack de servicii se utilizeaza comanda:" ) add_code_block(doc, "docker compose up -d") p = doc.add_paragraph() p.add_run("Pentru oprirea serviciilor:") add_code_block(doc, "docker compose down") p = doc.add_paragraph() p.add_run("Pentru vizualizarea log-urilor:") add_code_block(doc, "docker compose logs -f erpnext") doc.add_heading("13.2 Containere", level=2) p = doc.add_paragraph() p.add_run( "Stack-ul Docker Compose include urmatoarele containere:" ) add_table_with_header( doc, ["Container", "Serviciu", "Port", "Descriere"], [ ["erpnext", "ERPNext Web", "8080", "Interfata web si API ERPNext"], ["mariadb", "MariaDB", "3306", "Baza de date relationala"], ["redis-cache", "Redis Cache", "6379", "Cache pentru sesiuni si date frecvente"], ["redis-queue", "Redis Queue", "6380", "Coada de mesaje pentru joburi asincrone"], ["worker-default", "Worker Default", "-", "Procesare joburi standard"], ["worker-short", "Worker Short", "-", "Procesare joburi rapide"], ["worker-long", "Worker Long", "-", "Procesare joburi de durata lunga"], ["scheduler", "Scheduler", "-", "Planificator taskuri recurente (cron-like)"], ], ) doc.add_heading("13.3 Backup si Restaurare", level=2) p = doc.add_paragraph() p.add_run( "Datele ERPNext sunt persistate in volume Docker, asigurand pastrarea informatiilor " "intre repornirile containerelor. Pentru backup se recomanda:" ) add_bullet_list(doc, [ "Backup automat zilnic al bazei de date MariaDB folosind cron job-ul integrat in scheduler", "Export periodic al fisierelor uploadate (volume Docker)", "Stocare backup-uri pe un server extern sau serviciu cloud (offsite backup)", "Testarea periodica a procesului de restaurare din backup", ]) p = doc.add_paragraph() p.add_run("Comanda pentru backup manual:") add_code_block(doc, "docker compose exec erpnext bench --site frontend backup --with-files") doc.add_page_break() # ========================================================================= # CHAPTER 14: Securitate # ========================================================================= doc.add_heading("14. Securitate", level=1) doc.add_heading("14.1 API User (Utilizator pentru Integrari)", level=2) p = doc.add_paragraph() p.add_run( "Comunicarea intre website-ul Next.js si ERPNext se realizeaza printr-un " "utilizator API dedicat, configurat cu permisiuni minimale (principiul " "least privilege):" ) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Utilizator", "website_api@didi.localhost"], ["Tip", "API User (System User fara Desk Access)"], ["Rol", "Website Integration"], ["Autentificare", "API Key + API Secret (token-based)"], ["Desk Access", "Dezactivat (fara acces la interfata web ERPNext)"], ], ) doc.add_heading("14.2 Rol Website Integration", level=2) p = doc.add_paragraph() p.add_run( "Rolul Website Integration a fost creat special pentru utilizatorul API si ofera " "acces limitat doar la operatiunile necesare integrarii:" ) add_bullet_list(doc, [ "Customer - citire si creare (pentru inregistrarea clientilor noi)", "Sales Invoice - citire si creare (pentru generarea facturilor)", "Payment Entry - citire si creare (pentru inregistrarea platilor)", "Service Agreement - citire, creare si modificare (pentru gestionarea drepturilor)", "Payment Log - citire si creare (pentru audit trail plati)", "Analysis Report - citire, creare si modificare (pentru rapoarte analiza)", "Website Content - doar citire (pentru afisarea continutului pe website)", "Lead - citire si creare (pentru captarea lead-urilor din formular contact)", "Item - doar citire (pentru afisarea catalogului de servicii)", ]) doc.add_heading("14.3 Recomandari de Securitate", level=2) add_bullet_list(doc, [ "Schimbati parola implicita 'admin' la prima accesare", "Activati autentificarea in doi factori (2FA) pentru contul Administrator", "Revizuiti periodic permisiunile rolurilor si utilizatorilor", "Monitorizati log-urile de acces pentru activitati suspecte", "Actualizati ERPNext la cele mai recente versiuni de securitate", "Nu expuneti portul 8080 direct pe internet - utilizati reverse proxy cu HTTPS", "Rotiti periodic API Key-urile utilizatorului de integrare", ]) # Save document output_path = os.path.join(OUTPUT_DIR, "Ghid_Administrare_ERPNext.docx") doc.save(output_path) print(f" -> Salvat: {output_path}") return output_path # ============================================================================== # DOCUMENT 3: Documentatie API # ============================================================================== def generate_documentatie_api(): """Generate Documentatie_API.docx""" print("[3/4] Generare Documentatie_API.docx ...") doc = create_document() add_cover_page( doc, "DOCUMENTATIE TEHNICA API", "Integrari Website DiDi", ) add_header_footer(doc) add_toc_placeholder(doc) # ========================================================================= # CHAPTER 1: Introducere # ========================================================================= doc.add_heading("1. Introducere", level=1) doc.add_heading("1.1 Scopul Documentului", level=2) p = doc.add_paragraph() p.add_run( "Aceasta documentatie tehnica descrie in detaliu toate intergrarile API utilizate " "de website-ul DiDi. Documentul acopera: rutele API ale website-ului Next.js, " "integrarea cu ERPNext, integrarea cu DiDi AI API, integrarea cu Stripe si " "integrarea cu Keycloak SSO." ) doc.add_heading("1.2 Audienta Tinta", level=2) p = doc.add_paragraph() p.add_run( "Documentul se adreseaza dezvoltatorilor software, arhitectilor de sistem si " "personalului tehnic responsabil cu mentenanta si extinderea platformei DiDi." ) doc.add_heading("1.3 Conventii", level=2) add_bullet_list(doc, [ "Endpoint-urile sunt descrise in formatul: METODA /cale", "Parametrii obligatorii sunt marcati cu * (asterisk)", "Raspunsurile sunt in format JSON daca nu se specifica altfel", "Codurile de status HTTP urmeaza standardul RFC 7231", ]) doc.add_page_break() # ========================================================================= # CHAPTER 2: Arhitectura Generala # ========================================================================= doc.add_heading("2. Arhitectura Generala", level=1) p = doc.add_paragraph() p.add_run( "Arhitectura de integrari a platformei DiDi urmeaza un model hub-and-spoke, " "in care website-ul Next.js actioneaza ca hub central, comunicand cu toate " "serviciile externe prin API routes server-side." ) doc.add_heading("2.1 Diagrama Integrari", level=2) add_code_block( doc, """ +-------------------+ +-------------------+ | | OIDC | | | Client Browser +--------->+ Keycloak SSO | | (React/Next.js) | JWT | didi365.eu/auth | | |<---------+ | +--------+----------+ +-------------------+ | | HTTPS (API Routes) v +--------+----------+ +-------------------+ | | REST | | | Next.js Server +--------->+ ERPNext v15 API | | (API Routes) | Token | localhost:8080 | | |<---------+ | +--------+----------+ +-------------------+ | | | | Webhook | v | +----+-------------+ | | | | | Stripe API | | | Checkout + | | | Webhooks | | +------------------+ | | REST API v +---+---------------+ +-------------------+ | | | | | DiDi AI API +--------->+ MinIO Storage | | didi365.eu/api | | (Media Upload) | | | +-------------------+ +-------------------+ """, ) doc.add_heading("2.2 Principii de Design", level=2) add_bullet_list(doc, [ "Server-side proxy: toate apelurile catre servicii externe se fac din server-ul Next.js, nu din browser. Credentialele (API keys, secrets) nu sunt niciodata expuse in client-side code.", "Token-based auth: comunicarea cu ERPNext utilizeaza API Key + Secret, comunicarea cu Keycloak utilizeaza JWT tokens, comunicarea cu Stripe utilizeaza Secret Key.", "Webhook verification: toate webhook-urile primite (Stripe) sunt verificate prin semnatura criptografica inainte de procesare.", "Error handling: toate erorile sunt tratate graceful cu raspunsuri HTTP corespunzatoare si mesaje descriptive.", ]) doc.add_page_break() # ========================================================================= # CHAPTER 3: Autentificare # ========================================================================= doc.add_heading("3. Autentificare", level=1) doc.add_heading("3.1 Keycloak SSO", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Server", "https://didi365.eu/didi-auth"], ["Realm", "didi-website"], ["Client ID", "didi-website-server"], ["Client Type", "Confidential (cu client secret)"], ["Protocol", "OpenID Connect"], ["Grant Type", "Authorization Code Flow"], ["Token Endpoint", "/realms/didi-website/protocol/openid-connect/token"], ["Auth Endpoint", "/realms/didi-website/protocol/openid-connect/auth"], ["Userinfo Endpoint", "/realms/didi-website/protocol/openid-connect/userinfo"], ], ) p = doc.add_paragraph() p.add_run( "Autentificarea pe website este gestionata prin Auth.js v5 (NextAuth) cu " "provider-ul Keycloak. La autentificarea reusita, Auth.js creeaza o sesiune " "server-side care contine: access_token, id_token, refresh_token si datele " "utilizatorului (sub, name, email)." ) doc.add_heading("3.2 ERPNext API Authentication", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Metoda", "Token-based (API Key + API Secret)"], ["User", "website_api@didi.localhost"], ["Rol", "Website Integration"], ["Header", "Authorization: token api_key:api_secret"], ], ) p = doc.add_paragraph() p.add_run( "Fiecare request catre ERPNext API include header-ul Authorization cu token-ul " "format din API Key si API Secret separate prin doua puncte (:). Acest mecanism " "este configurat in fisierul src/lib/erpnext.ts." ) doc.add_heading("3.3 Stripe Authentication", level=2) add_table_with_header( doc, ["Parametru", "Utilizare"], [ ["Publishable Key (pk_test_*)", "Client-side: initializare Stripe.js (nu se foloseste direct)"], ["Secret Key (sk_test_*)", "Server-side: creare sesiuni checkout, gestionare plati"], ["Webhook Secret (whsec_*)", "Server-side: verificare semnatura webhook-uri"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 4: API Routes Website (Next.js) # ========================================================================= doc.add_heading("4. API Routes Website (Next.js)", level=1) p = doc.add_paragraph() p.add_run( "Website-ul Next.js expune urmatoarele API routes server-side. Toate rutele " "sunt definite in directorul src/app/api/ si ruleaza exclusiv pe server." ) # 4.1 doc.add_heading("4.1 /api/auth/[...nextauth]", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/auth/[...nextauth]"], ["Metode", "GET, POST"], ["Autentificare", "Nu (este endpoint-ul de autentificare)"], ["Descriere", "Handler Auth.js pentru fluxul de autentificare Keycloak"], ], ) p = doc.add_paragraph() p.add_run( "Acest endpoint gestioneaza intregul flux de autentificare: redirect la Keycloak, " "callback cu authorization code, obtinere tokens, creare sesiune, refresh tokens " "si deconectare. Nu necesita implementare manuala - este gestionat complet de Auth.js." ) # 4.2 doc.add_heading("4.2 /api/erp/[...path]", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/erp/[...path]"], ["Metode", "GET, POST, PUT"], ["Autentificare", "Sesiune Auth.js obligatorie"], ["Descriere", "Proxy server-side catre ERPNext API"], ], ) p = doc.add_paragraph() p.add_run( "Actioneaza ca un proxy transparent intre client-side si ERPNext. Primeste request-uri " "de la browser, adauga header-ul de autentificare ERPNext (API Key:Secret) si " "retrimite request-ul catre ERPNext. Raspunsul ERPNext este returnat clientului. " "Acest mecanism asigura ca credentialele ERPNext nu sunt niciodata expuse in browser." ) # 4.3 doc.add_heading("4.3 /api/checkout", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/checkout"], ["Metoda", "POST"], ["Autentificare", "Sesiune Auth.js obligatorie"], ["Content-Type", "application/json"], ], ) doc.add_heading("Request Body", level=3) add_code_block( doc, """ { "priceId": "price_xxx", // Stripe Price ID "itemCode": "DIDI-TECHNIQUES-TEXT", // Cod articol ERPNext "customerName": "John Doe", // Numele clientului "customerEmail": "john@example.com" // Email client } """, ) doc.add_heading("Response (200)", level=3) add_code_block( doc, """ { "sessionId": "cs_xxx", // Stripe Checkout Session ID "url": "https://checkout.stripe.com/..." // URL redirect Stripe } """, ) p = doc.add_paragraph() p.add_run( "Creeaza o sesiune Stripe Checkout cu pretul si detaliile serviciului selectat. " "Returneaza URL-ul de redirect catre pagina de plata Stripe." ) doc.add_page_break() # 4.4 doc.add_heading("4.4 /api/webhooks/stripe", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/webhooks/stripe"], ["Metoda", "POST"], ["Autentificare", "Stripe Webhook Signature (whsec_*)"], ["Content-Type", "application/json (raw body)"], ], ) p = doc.add_paragraph() p.add_run( "Primeste notificari webhook de la Stripe la finalizarea platilor. Procesul de " "fulfillment declansat include:" ) add_numbered_list(doc, [ "Verificarea semnaturii webhook-ului pentru autenticitate", "Extragerea datelor tranzactiei din evenimentul checkout.session.completed", "Crearea sau identificarea clientului in ERPNext (pe baza didi_user_id)", "Crearea facturii (Sales Invoice) cu articolul achizitionat, auto-submit", "Crearea inregistrarii de plata (Payment Entry) pentru reconciliere contabila", "Crearea acordului de servicii (Service Agreement) care activeaza analiza", "Actualizarea planului clientului (iam_role) daca este necesar", "Crearea intrarii in jurnalul de plati (Payment Log) pentru audit", ]) # 4.5 doc.add_heading("4.5 /api/analyze", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/analyze"], ["Metode", "GET, POST"], ["Autentificare", "Sesiune Auth.js obligatorie"], ], ) doc.add_heading("GET /api/analyze", level=3) p = doc.add_paragraph() p.add_run( "Returneaza lista analizelor disponibile (Service Agreements active) ale clientului " "curent. Daca se furnizeaza parametrul session_id, returneaza statusul/rezultatul " "unei analize in procesare (polling)." ) doc.add_heading("POST /api/analyze", level=3) p = doc.add_paragraph() p.add_run( "Porneste o noua analiza. Accepta doua formate de input:" ) add_bullet_list(doc, [ "JSON body: { text, language, category, mediaType, serviceAgreement } - pentru analize text/URL", "FormData (multipart): file + category + mediaType + serviceAgreement - pentru upload fisiere media", ]) add_code_block( doc, """ // Exemplu request text: POST /api/analyze Content-Type: application/json { "text": "Textul de analizat...", "language": "ro", "category": "techniques", "mediaType": "text", "serviceAgreement": "SA-00001" } // Exemplu request media (upload fisier): POST /api/analyze Content-Type: multipart/form-data - file: [fisier imagine/audio/video] - category: "ai_detection" - mediaType: "image" - serviceAgreement: "SA-00002" """, ) doc.add_page_break() # 4.6 doc.add_heading("4.6 /api/analysis-reports", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/analysis-reports"], ["Metode", "GET, POST"], ["Autentificare", "Sesiune Auth.js obligatorie"], ], ) p = doc.add_paragraph() p.add_run( "GET: Returneaza lista rapoartelor de analiza salvate ale clientului curent. " "POST: Creeaza un nou raport de analiza in ERPNext cu rezultatele obtinute de la DiDi API. " "Raportul include: titlu, client, factura, session_id, result_json, status." ) # 4.7 doc.add_heading("4.7 /api/analysis-report-pdf", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/analysis-report-pdf"], ["Metoda", "GET"], ["Autentificare", "Sesiune Auth.js obligatorie"], ["Parametri", "?report=REPORT_NAME"], ["Response", "application/pdf"], ], ) p = doc.add_paragraph() p.add_run( "Genereaza si returneaza un fisier PDF cu raportul complet al analizei, " "formatat profesional cu rezultatele, scorurile si recomandarile." ) # 4.8 doc.add_heading("4.8 /api/customer/me", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/customer/me"], ["Metoda", "GET"], ["Autentificare", "Sesiune Auth.js obligatorie"], ], ) p = doc.add_paragraph() p.add_run( "Identifica si returneaza clientul ERPNext asociat sesiunii Keycloak curente. " "Cautarea se face pe baza campului didi_user_id (UUID Keycloak) din fisa clientului ERPNext." ) # 4.9 doc.add_heading("4.9 /api/leads", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/leads"], ["Metoda", "POST"], ["Autentificare", "Nu (endpoint public)"], ["Content-Type", "application/json"], ], ) add_code_block( doc, """ { "name": "Ion Popescu", "email": "ion@example.com", "message": "Doresc o demonstratie a platformei", "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "launch_2026" } """, ) p = doc.add_paragraph() p.add_run( "Creeaza un Lead in ERPNext CRM cu datele din formularul de contact. " "Include parametrii UTM pentru tracking-ul sursei de trafic." ) # 4.10 doc.add_heading("4.10 /api/invoice-pdf", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Cale", "/api/invoice-pdf"], ["Metoda", "GET"], ["Autentificare", "Sesiune Auth.js obligatorie"], ["Parametri", "?invoice=SINV-XXXXX"], ["Response", "application/pdf"], ], ) p = doc.add_paragraph() p.add_run( "Descarca factura in format PDF de la ERPNext, folosind print format-ul " "personalizat DiDi Invoice. Verifica ca factura apartine clientului curent " "inainte de returnare." ) doc.add_page_break() # ========================================================================= # CHAPTER 5: Integrare DiDi API # ========================================================================= doc.add_heading("5. Integrare DiDi API", level=1) p = doc.add_paragraph() p.add_run( "DiDi API este serviciul de inteligenta artificiala care proceseaza analizele de " "continut. API-ul este disponibil la adresa https://didi365.eu/didi-api si ofera " "endpoint-uri pentru upload media si 4 tipuri de analiza." ) doc.add_heading("5.1 Upload Media", level=2) add_table_with_header( doc, ["Proprietate", "Valoare"], [ ["Endpoint", "POST /v3/media/upload"], ["Content-Type", "multipart/form-data"], ["Camp fisier", "file"], ["Raspuns", "{ public_url: 'https://...' }"], ], ) p = doc.add_paragraph() p.add_run( "Incarca un fisier media (imagine, audio, video) in storage-ul MinIO si returneaza " "URL-ul public al fisierului. Acest URL este apoi utilizat in request-urile de analiza." ) doc.add_heading("5.2 Endpoint-uri de Analiza", level=2) add_table_with_header( doc, ["Endpoint", "Categorie", "Descriere"], [ ["POST /v3/techniques/analyze", "Detectie Tehnici", "Identificare tehnici manipulare si propaganda"], ["POST /v3/ai-tampered/analyze", "AI Detection", "Detectie continut generat/modificat de AI"], ["POST /v3/claims/analyze", "Fact-Checking", "Verificare afirmatii si informatii"], ["POST /v3/domain/analyze", "Evaluare Surse", "Analiza credibilitate domenii web"], ], ) doc.add_heading("5.3 Payload pentru Text", level=2) add_code_block( doc, """ POST /v3/techniques/analyze Content-Type: application/json { "text": "Textul de analizat pentru detectarea tehnicilor de manipulare...", "language": "ro" } """, ) doc.add_heading("5.4 Payload pentru Media", level=2) add_code_block( doc, """ POST /v3/ai-tampered/analyze Content-Type: application/json { "media_type": "image", // "image" | "audio" | "video" "media_url": "https://storage.didi365.eu/uploads/file.jpg" } """, ) doc.add_heading("5.5 Raspuns Sincron (Text/URL/Imagine)", level=2) p = doc.add_paragraph() p.add_run( "Pentru analizele de tip text, URL si imagine, API-ul returneaza rezultatul " "direct in raspunsul HTTP cu status 200:" ) add_code_block( doc, """ HTTP 200 OK { "risk_score": 0.78, "confidence": 0.92, "risk_category": "high", "techniques": [ { "name": "appeal_to_emotion", "score": 0.85 }, { "name": "cherry_picking", "score": 0.72 } ], "verdict": "Continutul prezinta indicii puternice de manipulare...", "claims": [...], "ai_tampered": { "probability": 0.15, "verdict": "authentic" }, "domain": { "credibility_score": 0.45, "category": "unreliable" } } """, ) doc.add_page_break() doc.add_heading("5.6 Raspuns Asincron (Audio/Video)", level=2) p = doc.add_paragraph() p.add_run( "Pentru analizele de tip audio si video, API-ul returneaza un status 202 " "cu un session_id pentru polling ulterior:" ) add_code_block( doc, """ HTTP 202 Accepted { "session_id": "sess_abc123def456", "status": "queued", "message": "Analysis queued for processing" } """, ) doc.add_heading("5.7 Polling pentru Rezultate", level=2) p = doc.add_paragraph() p.add_run( "Dupa primirea unui raspuns 202, clientul trebuie sa faca polling pentru " "a verifica statusul procesarii:" ) add_code_block( doc, """ // Pasul 1: Verificare status GET /v3/pipeline/{session_id}/queue-status Raspunsuri posibile: { "status": "queued" } // In coada de procesare { "status": "processing" } // In curs de procesare { "status": "completed" } // Procesare finalizata // Pasul 2: Obtinere rezultat (dupa status "completed") GET /v3/pipeline/{session_id}/result Raspuns: aceeasi structura ca la raspunsul sincron (5.5) """, ) doc.add_heading("5.8 Structura Rezultat", level=2) add_table_with_header( doc, ["Camp", "Tip", "Descriere"], [ ["risk_score", "float (0-1)", "Scorul de risc de dezinformare (0=fara risc, 1=risc maxim)"], ["confidence", "float (0-1)", "Nivelul de incredere al algoritmului in rezultat"], ["risk_category", "string", "Categorie: low / medium / high / critical"], ["techniques", "array", "Lista tehnicilor de manipulare detectate cu scoruri individuale"], ["verdict", "string", "Concluzia generala a analizei in text descriptiv"], ["claims", "array", "Lista afirmatiilor verificate (fact-checking)"], ["ai_tampered", "object", "Rezultat detectie AI: probability, verdict"], ["domain", "object", "Evaluare domeniu: credibility_score, category"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 6: Integrare Stripe # ========================================================================= doc.add_heading("6. Integrare Stripe", level=1) doc.add_heading("6.1 PRICE_MAP - Mapare Servicii", level=2) p = doc.add_paragraph() p.add_run( "Fiecare serviciu DiDi este mapat la un Stripe Price ID prin constanta PRICE_MAP " "definita in configuratia aplicatiei. Aceasta mapare asociaza 16 combinatii de " "categorii si tipuri de media cu price ID-urile corespunzatoare din Stripe:" ) add_code_block( doc, """ const PRICE_MAP = { "DIDI-TECHNIQUES-TEXT": "price_techniques_text", "DIDI-TECHNIQUES-IMAGE": "price_techniques_image", "DIDI-TECHNIQUES-AUDIO": "price_techniques_audio", "DIDI-TECHNIQUES-VIDEO": "price_techniques_video", "DIDI-AI_DETECTION-TEXT": "price_ai_detection_text", "DIDI-AI_DETECTION-IMAGE": "price_ai_detection_image", "DIDI-AI_DETECTION-AUDIO": "price_ai_detection_audio", "DIDI-AI_DETECTION-VIDEO": "price_ai_detection_video", "DIDI-CLAIMS-TEXT": "price_claims_text", "DIDI-CLAIMS-IMAGE": "price_claims_image", "DIDI-CLAIMS-AUDIO": "price_claims_audio", "DIDI-CLAIMS-VIDEO": "price_claims_video", "DIDI-SOURCE-TEXT": "price_source_text", "DIDI-SOURCE-IMAGE": "price_source_image", "DIDI-SOURCE-AUDIO": "price_source_audio", "DIDI-SOURCE-VIDEO": "price_source_video" }; """, ) doc.add_heading("6.2 Checkout Flow", level=2) p = doc.add_paragraph() p.add_run( "Fluxul de checkout Stripe se desfasoara in 3 etape principale:" ) add_numbered_list(doc, [ "Creare sesiune: POST /api/checkout creeaza un Stripe Checkout Session cu price ID-ul, datele clientului si URL-urile de redirect (success/cancel)", "Redirect Stripe: utilizatorul este redirectionat la checkout.stripe.com unde completeaza datele de plata in mediul securizat Stripe", "Webhook fulfillment: dupa plata reusita, Stripe trimite evenimentul checkout.session.completed la /api/webhooks/stripe care declanseaza procesul de fulfillment (creare factura, plata, acord servicii)", ]) doc.add_heading("6.3 Webhook Events", level=2) add_table_with_header( doc, ["Eveniment", "Actiune"], [ ["checkout.session.completed", "Fulfillment complet: factura + plata + acord servicii + payment log"], ], ) doc.add_heading("6.4 Fulfillment (Procesare Automata)", level=2) p = doc.add_paragraph() p.add_run( "La primirea evenimentului checkout.session.completed, webhook-ul declanseaza " "urmatoarele operatiuni in ordine secventiala:" ) add_numbered_list(doc, [ "Creare/identificare customer - Cautare client ERPNext dupa didi_user_id (Keycloak UUID). Daca nu exista, se creeaza automat cu datele din sesiunea Stripe.", "Creare factura (Sales Invoice) - Se genereaza automat cu articolul achizitionat, pret, TVA 19%. Factura se valideaza automat (auto-submit) pentru a fi considerata emisa.", "Creare incasare (Payment Entry) - Se inregistreaza plata in contabilitate, reconciliata cu factura emisa. Contul de incasari: Stripe.", "Creare acord servicii (Service Agreement) - Se activeaza dreptul clientului de a utiliza serviciul achizitionat. Status: Activ.", "Actualizare plan client - Se actualizeaza campul iam_role al clientului (paid_tier daca era free_tier).", "Creare Payment Log - Se inregistreaza tranzactia in jurnalul de plati cu toate detaliile Stripe pentru audit complet.", ]) doc.add_page_break() # ========================================================================= # CHAPTER 7: Integrare ERPNext # ========================================================================= doc.add_heading("7. Integrare ERPNext", level=1) doc.add_heading("7.1 DocTypes Custom", level=2) p = doc.add_paragraph() p.add_run( "Aplicatia custom didi_custom adauga 4 DocTypes noi in ERPNext, specifice " "functionalitatilor platformei DiDi:" ) add_table_with_header( doc, ["DocType", "Modul", "Descriere"], [ ["Service Agreement", "DiDi", "Acord prestare servicii - activeaza dreptul de a rula o analiza"], ["Payment Log", "DiDi", "Jurnal plati Stripe - audit trail complet al tranzactiilor"], ["Website Content", "DiDi", "Continut website CMS - texte pagini bilingv RO/EN"], ["Analysis Report", "DiDi", "Raport analiza AI - rezultate complete analiza cu JSON si PDF"], ], ) doc.add_heading("7.2 Custom Fields pe DocTypes Existente", level=2) doc.add_heading("Customer (Client)", level=3) add_table_with_header( doc, ["Fieldname", "Label", "Tip", "Descriere"], [ ["didi_user_id", "ID Utilizator DiDi", "Data", "UUID Keycloak pentru legatura cu contul de autentificare"], ["iam_role", "Rol IAM", "Select", "Nivelul de acces: free_tier / paid_tier / enterprise_tier"], ["active_plan", "Plan Activ", "Data", "Planul de abonament curent"], ], ) doc.add_heading("Lead", level=3) add_table_with_header( doc, ["Fieldname", "Label", "Tip", "Descriere"], [ ["utm_source", "UTM Source", "Data", "Sursa traficului (google, facebook, etc.)"], ["utm_medium", "UTM Medium", "Data", "Mediul de marketing (cpc, organic, email)"], ["utm_campaign", "UTM Campaign", "Data", "Campania specifica"], ], ) doc.add_heading("Sales Invoice (Factura)", level=3) add_table_with_header( doc, ["Fieldname", "Label", "Tip", "Descriere"], [ ["analysis_consumed", "Analiza Consumata", "Check", "Indica daca analiza asociata a fost rulata"], ["analysis_session_id", "Session ID Analiza", "Data", "ID-ul sesiunii de analiza DiDi API"], ], ) doc.add_heading("7.3 Endpoint-uri ERPNext Utilizate", level=2) add_table_with_header( doc, ["Endpoint", "Metoda", "Descriere"], [ ["/api/resource/{DocType}", "GET", "Listare si cautare documente"], ["/api/resource/{DocType}/{name}", "GET", "Obtinere detalii document"], ["/api/resource/{DocType}", "POST", "Creare document nou"], ["/api/resource/{DocType}/{name}", "PUT", "Actualizare document existent"], ["/api/method/upload_file", "POST", "Upload fisier (atasament)"], ["/api/method/frappe.client.submit", "POST", "Validare (submit) document"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 8: Fisiere Cheie Cod Sursa # ========================================================================= doc.add_heading("8. Fisiere Cheie Cod Sursa", level=1) doc.add_heading("8.1 Librarii Partajate (src/lib/)", level=2) add_table_with_header( doc, ["Fisier", "Descriere"], [ ["src/lib/auth.ts", "Configurare Auth.js v5 cu Keycloak provider, callbacks JWT/session"], ["src/lib/erpnext.ts", "Client ERPNext API: functii CRUD, autentificare token, error handling"], ["src/lib/stripe.ts", "Initializare Stripe SDK cu secret key"], ["src/lib/stripe-fulfillment.ts", "Logica completa de fulfillment: creare customer, invoice, payment, SA"], ["src/lib/api.ts", "Client DiDi API: upload media, analiza, polling, parsing rezultate"], ["src/lib/analysis-report-pdf.ts", "Generator PDF rapoarte analiza cu formatare profesionala"], ["src/lib/analysis-report-store.ts", "CRUD operatii pentru Analysis Report in ERPNext"], ], ) doc.add_heading("8.2 API Routes (src/app/api/)", level=2) add_table_with_header( doc, ["Fisier", "Route", "Descriere"], [ ["src/app/api/auth/[...nextauth]/route.ts", "/api/auth/*", "Handler Auth.js complet"], ["src/app/api/erp/[...path]/route.ts", "/api/erp/*", "Proxy ERPNext server-side"], ["src/app/api/checkout/route.ts", "/api/checkout", "Creare Stripe Checkout Session"], ["src/app/api/webhooks/stripe/route.ts", "/api/webhooks/stripe", "Procesare webhook Stripe"], ["src/app/api/analyze/route.ts", "/api/analyze", "Rulare si polling analize AI"], ["src/app/api/analysis-reports/route.ts", "/api/analysis-reports", "CRUD rapoarte analiza"], ["src/app/api/analysis-report-pdf/route.ts", "/api/analysis-report-pdf", "Download PDF raport"], ["src/app/api/customer/me/route.ts", "/api/customer/me", "Identificare client curent"], ["src/app/api/leads/route.ts", "/api/leads", "Creare Lead CRM"], ["src/app/api/invoice-pdf/route.ts", "/api/invoice-pdf", "Download PDF factura"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 9: Variabile de Mediu # ========================================================================= doc.add_heading("9. Variabile de Mediu", level=1) p = doc.add_paragraph() p.add_run( "Urmatoarele variabile de mediu trebuie configurate in fisierul .env.local " "pentru functionarea corecta a website-ului:" ) add_table_with_header( doc, ["Variabila", "Descriere", "Exemplu"], [ ["NEXTAUTH_URL", "URL-ul de baza al website-ului", "https://didi365.eu"], ["NEXTAUTH_SECRET", "Secret pentru criptarea sesiunilor Auth.js", "[random string 32+ chars]"], ["KEYCLOAK_ISSUER", "URL-ul issuer-ului Keycloak", "https://didi365.eu/didi-auth/realms/didi-website"], ["KEYCLOAK_CLIENT_ID", "Client ID pentru Keycloak", "didi-website-server"], ["KEYCLOAK_CLIENT_SECRET", "Client Secret pentru Keycloak", "[UUID from Keycloak]"], ["ERPNEXT_URL", "URL-ul instanta ERPNext", "http://localhost:8080"], ["ERPNEXT_API_KEY", "API Key utilizator integrare", "[from ERPNext]"], ["ERPNEXT_API_SECRET", "API Secret utilizator integrare", "[from ERPNext]"], ["STRIPE_SECRET_KEY", "Stripe Secret Key", "sk_test_..."], ["STRIPE_PUBLISHABLE_KEY", "Stripe Publishable Key", "pk_test_..."], ["STRIPE_WEBHOOK_SECRET", "Secret verificare webhook", "whsec_..."], ["DIDI_API_URL", "URL-ul DiDi AI API", "https://didi365.eu/didi-api"], ["DIDI_API_KEY", "Cheie acces DiDi API", "[API key]"], ], ) add_note_box( doc, "Fisierul .env.local nu trebuie niciodata comis in repository-ul Git. " "Asigurati-va ca este inclus in .gitignore. Valorile secrete trebuie " "stocate intr-un manager de secrete (ex: Docker secrets, Vault).", "warning", ) # Save document output_path = os.path.join(OUTPUT_DIR, "Documentatie_API.docx") doc.save(output_path) print(f" -> Salvat: {output_path}") return output_path # ============================================================================== # DOCUMENT 4: Arhitectura Detaliata # ============================================================================== def generate_arhitectura(): """Generate Arhitectura_Detaliata.docx""" print("[4/4] Generare Arhitectura_Detaliata.docx ...") doc = create_document() add_cover_page( doc, "ARHITECTURA DETALIATA", "Platforma DiDi Website + ERP/CRM", ) add_header_footer(doc) add_toc_placeholder(doc) # ========================================================================= # CHAPTER 1: Prezentare Generala # ========================================================================= doc.add_heading("1. Prezentare Generala", level=1) p = doc.add_paragraph() p.add_run( "Platforma DiDi este o solutie digitala complexa dezvoltata in cadrul proiectului " "PNRR Contract 11.1.i3.c9/10.05.2024, care integreaza multiple componente tehnologice " "pentru a oferi servicii de detectare si combatere a dezinformarii." ) p = doc.add_paragraph() p.add_run("Platforma este compusa din urmatoarele componente principale:") add_bullet_list(doc, [ "Website Next.js 16 - interfata publica si dashboard client, cu e-commerce integrat", "ERPNext v15 ERP/CRM - sistem de management resurse, facturare, CRM si gestiune clienti", "Keycloak SSO - server de autentificare centralizata cu OpenID Connect", "Stripe - procesator de plati online pentru e-commerce", "DiDi AI API - motor de inteligenta artificiala pentru analiza continutului digital", "SendGrid - serviciu email transactional pentru notificari automate", ]) p = doc.add_paragraph() p.add_run( "Toate componentele sunt integrate prin API-uri REST si comunica prin protocoale " "securizate (HTTPS, JWT, API tokens). Arhitectura urmeaza principiile microserviciilor, " "cu fiecare componenta avand responsabilitati bine definite si independenta operationala." ) doc.add_page_break() # ========================================================================= # CHAPTER 2: Diagrama Componente # ========================================================================= doc.add_heading("2. Diagrama Componente", level=1) doc.add_heading("2.1 Diagrama de Nivel Inalt", level=2) add_code_block( doc, """ +===========================================================+ | CLIENT LAYER | | +-----------------------------------------------------+ | | | Client Browser (Desktop/Mobile) | | | | React Components + Tailwind CSS | | | +---------------------------+-------------------------+ | +===========================================================+ | | HTTPS v +===========================================================+ | APPLICATION LAYER | | +-----------------------------------------------------+ | | | Next.js 16 Website (App Router) | | | | | | | | +-------------+ +------------+ +--------------+ | | | | | Public Pages| | Dashboard | | API Routes | | | | | | (SSR/SSG) | | (Auth Req) | | (Server-side)| | | | | +-------------+ +------------+ +--------------+ | | | +-----------------------------------------------------+ | +===========================================================+ | | | | v v v v +==============+ +=============+ +============+ +============+ | Keycloak SSO | | ERPNext v15 | | Stripe API | | DiDi AI API| | (didi365.eu | | (Docker) | | (Payments) | | (didi365.eu| | /didi-auth) | | | | | | /didi-api) | +==============+ +=============+ +============+ +============+ | | v v +=============+ +============+ | MariaDB | | MinIO | | Redis x2 | | RabbitMQ | | Workers x3 | | Redis | | Scheduler | | PostgreSQL | +=============+ +============+ """, ) doc.add_heading("2.2 Fluxul de Comunicare", level=2) p = doc.add_paragraph() p.add_run( "Toate comunicarile intre componente se realizeaza prin protocolul HTTPS, cu exceptia " "comunicarilor interne intre containerele Docker din acelasi stack (ERPNext <-> MariaDB, " "DiDi API <-> PostgreSQL) care utilizeaza retele Docker interne. Fiecare integrare " "utilizeaza mecanisme de autentificare specifice (JWT, API tokens, webhook signatures)." ) doc.add_page_break() # ========================================================================= # CHAPTER 3: Componenta Website # ========================================================================= doc.add_heading("3. Componenta Website (Next.js 16)", level=1) doc.add_heading("3.1 Stack Tehnologic", level=2) add_table_with_header( doc, ["Tehnologie", "Versiune", "Utilizare"], [ ["Next.js", "16", "Framework React cu App Router, SSR, SSG, API Routes"], ["TypeScript", "5.x", "Tipizare statica pentru calitate si mentenabilitate cod"], ["Tailwind CSS", "3.x", "Framework CSS utility-first pentru styling rapid si consistent"], ["Auth.js", "v5 (NextAuth)", "Librarie autentificare cu provider Keycloak"], ["Stripe SDK", "Latest", "SDK oficial pentru integrarea platilor Stripe"], ["React", "19.x", "Librarie UI componente reactive"], ], ) doc.add_heading("3.2 Autentificare (Auth.js v5 cu Keycloak)", level=2) p = doc.add_paragraph() p.add_run( "Autentificarea este gestionata prin Auth.js v5 (fostul NextAuth.js) cu provider-ul " "Keycloak. Configurarea include:" ) add_bullet_list(doc, [ "Keycloak Provider cu OpenID Connect flow (Authorization Code)", "JWT callback pentru extragerea si stocarea tokens-urilor in sesiune", "Session callback pentru expunerea datelor utilizatorului catre componente", "Refresh token rotation pentru reinnoirea automata a sesiunilor expirate", "Configurare realm: didi-website, client: didi-website-server (confidential)", ]) doc.add_heading("3.3 Structura Routing", level=2) p = doc.add_paragraph() p.add_run( "Aplicatia Next.js utilizeaza App Router cu grupuri de rute (route groups) " "pentru organizarea logica a paginilor:" ) add_table_with_header( doc, ["Grup", "Prefix", "Autentificare", "Pagini"], [ ["(public)", "/", "Nu", "Homepage, Servicii, Despre, Pricing, Contact, Privacy, Terms"], ["(auth)", "/auth/*", "Nu (redirect daca autentificat)", "Login, Register, Callback"], ["(dashboard)", "/dashboard/*", "Da (redirect la login daca nu)", "Overview, Analize, Achizitii, Facturi, Profil"], ], ) doc.add_heading("3.4 API Routes Server-Side", level=2) p = doc.add_paragraph() p.add_run( "Toate intergrarile cu serviciile externe (ERPNext, Stripe, DiDi API, Keycloak) " "se realizeaza prin API routes server-side. Acest design asigura ca:" ) add_bullet_list(doc, [ "Credentialele (API keys, secrets, tokens) nu sunt niciodata expuse in codul client-side", "CORS nu este o problema deoarece request-urile se fac server-to-server", "Logica de business complexa (fulfillment, polling, PDF generation) ruleaza pe server", "Se poate implementa rate limiting si validare la nivel de server", ]) doc.add_heading("3.5 Internationalizare (i18n)", level=2) p = doc.add_paragraph() p.add_run( "Website-ul suporta doua limbi (RO/EN) implementate prin:" ) add_bullet_list(doc, [ "Continut pagini gestionat prin CMS ERPNext (Website Content) cu campuri bilingve", "Etichete UI stocate in fisiere de traducere locale (JSON)", "Selectorul de limba persista preferinta in localStorage", "Limba implicita: Romana (RO)", ]) doc.add_page_break() # ========================================================================= # CHAPTER 4: Componenta ERP/CRM # ========================================================================= doc.add_heading("4. Componenta ERP/CRM (ERPNext v15)", level=1) doc.add_heading("4.1 Infrastructura Docker", level=2) p = doc.add_paragraph() p.add_run( "ERPNext ruleaza intr-un stack Docker Compose cu 8 containere configurate " "pentru performanta si fiabilitate:" ) add_table_with_header( doc, ["Container", "Imagine", "Resurse", "Rol"], [ ["erpnext", "frappe/erpnext:v15", "Web + API", "Serveste interfata web si raspunde la API requests"], ["mariadb", "mariadb:10.11", "Baza de date", "Stocheaza toate datele ERPNext (documente, configurari)"], ["redis-cache", "redis:7", "Cache", "Cache pentru sesiuni, metadata si date frecvent accesate"], ["redis-queue", "redis:7", "Message Queue", "Coada de mesaje pentru job-uri asincrone (Frappe RQ)"], ["worker-default", "frappe/erpnext:v15", "Background Jobs", "Proceseaza job-uri standard (email, notificari)"], ["worker-short", "frappe/erpnext:v15", "Background Jobs", "Proceseaza job-uri rapide (sub 5 minute)"], ["worker-long", "frappe/erpnext:v15", "Background Jobs", "Proceseaza job-uri de durata lunga (rapoarte, export)"], ["scheduler", "frappe/erpnext:v15", "Task Scheduler", "Planifica si declanseaza taskuri recurente"], ], ) doc.add_heading("4.2 Aplicatie Custom: didi_custom", level=2) p = doc.add_paragraph() p.add_run( "Aplicatia Frappe custom didi_custom extinde ERPNext cu functionalitatile specifice " "platformei DiDi. Include 4 DocTypes noi si custom fields pe DocTypes existente:" ) add_table_with_header( doc, ["DocType", "Tip", "Campuri Principale"], [ ["Service Agreement", "Custom", "customer, item, invoice, status, created_date, expiry_date"], ["Payment Log", "Custom", "customer, event_type, status, amount, stripe_payment_id, stripe_session_id, invoice, raw_data"], ["Website Content", "Custom", "page_slug, content_ro, content_en, section, published"], ["Analysis Report", "Custom", "title, customer, invoice, session_id, analysis_type, media_type, result_json, status, pdf_attachment"], ], ) doc.add_heading("4.3 Configurare Companie", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Companie", "TOP CLOSSERS SRL"], ["CUI", "36193026"], ["Adresa", "Str. Targovistei 15, Ploiesti, 100299, Prahova"], ["Plan Conturi", "Romania Standard"], ["TVA", "19% (conform legislatie romaneasca)"], ["Moneda", "RON (Leu Romanesc)"], ["Serie Facturi", "DIDI-INV-YYYY-#####"], ["An Fiscal", "Ianuarie - Decembrie"], ], ) doc.add_heading("4.4 API User pentru Integrare", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Utilizator", "website_api@didi.localhost"], ["Tip", "System User (fara Desk Access)"], ["Rol", "Website Integration"], ["Autentificare", "API Key + API Secret"], ["Principiu", "Least Privilege - acces minim necesar"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 5: Integrari Externe # ========================================================================= doc.add_heading("5. Integrari Externe", level=1) doc.add_heading("5.1 Keycloak SSO", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Server", "https://didi365.eu/didi-auth"], ["Realm", "didi-website"], ["Protocol", "OpenID Connect (OIDC)"], ["Client", "didi-website-server (Confidential)"], ["Flow", "Authorization Code Flow"], ["Token Format", "JWT (JSON Web Token)"], ["Token Lifetime", "Access: 5 min, Refresh: 30 min, Session: 8 ore"], ], ) p = doc.add_paragraph() p.add_run( "Keycloak gestioneaza identitatea utilizatorilor, procesul de inregistrare, " "autentificare si managementul sesiunilor. Fiecare utilizator primeste un UUID " "unic care este utilizat ca didi_user_id in ERPNext pentru corelarea conturilor." ) doc.add_heading("5.2 Stripe (Plati Online)", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Tip Integrare", "Stripe Checkout (Hosted Payment Page)"], ["Tip Plati", "One-time (plata unica per serviciu)"], ["Moneda", "RON"], ["Webhook", "/api/webhooks/stripe"], ["Verificare", "Stripe Webhook Signature (HMAC SHA-256)"], ["Certificare", "PCI DSS Level 1"], ], ) doc.add_heading("5.3 DiDi AI API", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["URL", "https://didi365.eu/didi-api"], ["Versiune", "v3"], ["Autentificare", "API Key in header"], ["Endpoint-uri", "4 categorii analiza + upload media"], ["Storage", "MinIO (object storage S3-compatible)"], ["Queue", "RabbitMQ (procesare asincrona audio/video)"], ["Baza de date", "PostgreSQL + Redis"], ], ) doc.add_heading("5.4 SendGrid (Email Transactional)", level=2) add_table_with_header( doc, ["Parametru", "Valoare"], [ ["Provider", "SendGrid (Twilio)"], ["Protocol", "SMTP over TLS"], ["Server", "smtp.sendgrid.net:587"], ["Expeditor", "noreply@didi365.eu"], ["Utilizare", "Notificari automate: facturi, confirmari plata, expirari"], ], ) doc.add_page_break() # ========================================================================= # CHAPTER 6: Flux de Date # ========================================================================= doc.add_heading("6. Flux de Date", level=1) doc.add_heading("6.1 Flux Autentificare", level=2) add_code_block( doc, """ [Browser] --click Login--> [Next.js] [Next.js] --redirect--> [Keycloak Login Page] [User] --credentials--> [Keycloak] [Keycloak] --authorization code--> [Next.js callback] [Next.js] --exchange code--> [Keycloak Token Endpoint] [Keycloak] --JWT tokens--> [Next.js] [Next.js] --create session--> [Auth.js Session] [Next.js] --GET /api/customer/me--> [ERPNext] [ERPNext] --customer data (by didi_user_id)--> [Next.js] [Next.js] --render Dashboard--> [Browser] """, ) doc.add_heading("6.2 Flux Achizitie Serviciu", level=2) add_code_block( doc, """ [Browser] --selecteaza serviciu--> [Dashboard Achizitii] [Dashboard] --POST /api/checkout--> [Next.js API] [Next.js] --create Checkout Session--> [Stripe API] [Stripe] --session URL--> [Next.js] [Next.js] --redirect--> [Stripe Checkout Page] [User] --completeaza plata--> [Stripe] [Stripe] --redirect success--> [Website Success Page] [Stripe] --webhook POST--> [/api/webhooks/stripe] [Webhook] --create customer--> [ERPNext] [Webhook] --create invoice (auto-submit)--> [ERPNext] [Webhook] --create payment entry--> [ERPNext] [Webhook] --create service agreement--> [ERPNext] [Webhook] --update customer plan--> [ERPNext] [Webhook] --create payment log--> [ERPNext] [ERPNext] --send invoice email--> [SendGrid] --> [Client Email] """, ) doc.add_page_break() doc.add_heading("6.3 Flux Analiza Text (Sincron)", level=2) add_code_block( doc, """ [Browser] --selecteaza analiza--> [Dashboard Analize] [Browser] --introduce text--> [Formular Analiza] [Browser] --POST /api/analyze--> [Next.js API] [Next.js] --POST /v3/techniques/analyze--> [DiDi API] [DiDi API] --procesare AI--> [Rezultat] [DiDi API] --200 OK + result JSON--> [Next.js] [Next.js] --mark SA consumed--> [ERPNext] [Next.js] --create Analysis Report--> [ERPNext] [Next.js] --result JSON--> [Browser] [Browser] --afiseaza rezultat--> [Dashboard] """, ) doc.add_heading("6.4 Flux Analiza Media (Asincron)", level=2) add_code_block( doc, """ [Browser] --upload fisier--> [FormData] [Browser] --POST /api/analyze--> [Next.js API] [Next.js] --POST /v3/media/upload--> [DiDi API] [DiDi API] --stocare MinIO--> [public_url] [DiDi API] --public_url--> [Next.js] [Next.js] --POST /v3/ai-tampered/analyze { media_url }--> [DiDi API] [DiDi API] --202 Accepted { session_id }--> [Next.js] [Next.js] --session_id--> [Browser] [Browser] --polling GET /api/analyze?session_id--> [Next.js] [Next.js] --GET /v3/pipeline/{id}/queue-status--> [DiDi API] [DiDi API] --"completed"--> [Next.js] [Next.js] --GET /v3/pipeline/{id}/result--> [DiDi API] [DiDi API] --result JSON--> [Next.js] [Next.js] --persist result--> [ERPNext Analysis Report] [Next.js] --result--> [Browser] """, ) doc.add_heading("6.5 Flux CMS (Continut Website)", level=2) add_code_block( doc, """ [Admin] --editare continut--> [ERPNext Website Content] [ERPNext] --salveaza RO + EN--> [MariaDB] [Browser] --acceseaza pagina--> [Next.js] [Next.js] --GET /api/erp/Website Content?slug=homepage--> [ERPNext API] [ERPNext] --content_ro / content_en--> [Next.js] [Next.js] --selecteaza limba curenta--> [Render pagina] [Next.js] --HTML--> [Browser] """, ) doc.add_heading("6.6 Flux CRM (Lead Capture)", level=2) add_code_block( doc, """ [Vizitator] --completeaza formular contact--> [Browser] [Browser] --POST /api/leads { name, email, message, utm_* }--> [Next.js] [Next.js] --POST /api/resource/Lead--> [ERPNext API] [ERPNext] --create Lead (status: Open)--> [MariaDB] [ERPNext] --notify sales team--> [Email Notification] """, ) doc.add_page_break() # ========================================================================= # CHAPTER 7: Securitate # ========================================================================= doc.add_heading("7. Securitate", level=1) doc.add_heading("7.1 Transport Layer Security (TLS)", level=2) p = doc.add_paragraph() p.add_run( "Toate comunicarile externe utilizeaza HTTPS cu certificate TLS valide. " "Conexiunile HTTP sunt redirectionate automat catre HTTPS prin configurarea " "Nginx reverse proxy." ) doc.add_heading("7.2 Autentificare si Autorizare", level=2) add_table_with_header( doc, ["Mecanism", "Componenta", "Descriere"], [ ["JWT Tokens", "Keycloak", "Tokens cu durata limitata, semnate criptografic, refresh automat"], ["API Token", "ERPNext", "API Key + Secret, utilizator cu permisiuni minimale"], ["Webhook Signature", "Stripe", "HMAC SHA-256 pentru verificarea autenticitatii webhook-urilor"], ["API Key", "DiDi API", "Cheie de acces in header pentru autentificarea request-urilor"], ["Session Cookie", "Auth.js", "Cookie securizat (HttpOnly, Secure, SameSite) pentru sesiuni web"], ], ) doc.add_heading("7.3 RBAC (Role-Based Access Control)", level=2) p = doc.add_paragraph() p.add_run( "Platforma implementeaza 3 niveluri de acces (tiers) pentru utilizatori:" ) add_table_with_header( doc, ["Tier", "iam_role", "Acces", "Descriere"], [ ["Free", "free_tier", "Limitat", "Cont creat, fara achizitii. Acces la pagini publice si dashboard de baza"], ["Paid", "paid_tier", "Standard", "Cel putin o achizitie efectuata. Acces complet la analize si rapoarte"], ["Enterprise", "enterprise_tier", "Extins", "Client enterprise cu acces la functionalitati avansate si suport dedicat"], ], ) doc.add_heading("7.4 Server-Side API Proxy", level=2) p = doc.add_paragraph() p.add_run( "Toate apelurile catre servicii externe se fac exclusiv din server-ul Next.js. " "Credentialele (API keys, secrets, tokens) sunt stocate in variabile de mediu " "server-side si nu sunt niciodata incluse in bundle-ul JavaScript trimis la client. " "Aceasta arhitectura previne expunerea credentialelor prin inspectia codului sursa " "sau interceptarea traficului de retea din browser." ) doc.add_heading("7.5 Stripe PCI DSS Compliance", level=2) p = doc.add_paragraph() p.add_run( "Integrarea cu Stripe utilizeaza Stripe Checkout (hosted payment page), ceea ce " "inseamna ca datele de card sunt introduse exclusiv pe serverele Stripe si nu " "tranziteaza niciodata serverele platformei DiDi. Aceasta abordare asigura " "conformitatea cu standardul PCI DSS fara a necesita certificare PCI pe " "infrastructura proprie." ) doc.add_heading("7.6 GDPR Compliance", level=2) add_bullet_list(doc, [ "Cookie consent: banner de consimtamant la prima vizita cu optiuni granulare (necesar, functional, analitic)", "Politica de confidentialitate: document complet GDPR accesibil pe website", "Dreptul la acces: utilizatorii pot vizualiza datele personale din profilul dashboard", "Dreptul la rectificare: utilizatorii pot edita datele personale din profil", "Dreptul la stergere: utilizatorii pot solicita stergerea contului si a datelor asociate", "Dreptul la portabilitate: export date in format standard", "Baza legala: consimtamant explicit pentru prelucrare date", ]) doc.add_page_break() # ========================================================================= # CHAPTER 8: Deployment # ========================================================================= doc.add_heading("8. Deployment", level=1) doc.add_heading("8.1 Docker Compose", level=2) p = doc.add_paragraph() p.add_run( "Intreaga platforma este containerizata cu Docker si orchestrata prin Docker Compose. " "Fiecare componenta ruleaza in containere izolate cu volume persistente pentru date." ) doc.add_heading("Stack-ul de Containere ERPNext", level=3) add_code_block( doc, """ services: erpnext: image: frappe/erpnext:v15 ports: ["8080:8080"] volumes: [sites:/home/frappe/frappe-bench/sites] mariadb: image: mariadb:10.11 volumes: [mariadb-data:/var/lib/mysql] redis-cache: image: redis:7 redis-queue: image: redis:7 worker-default: image: frappe/erpnext:v15 command: bench worker --queue default worker-short: image: frappe/erpnext:v15 command: bench worker --queue short worker-long: image: frappe/erpnext:v15 command: bench worker --queue long scheduler: image: frappe/erpnext:v15 command: bench schedule """, ) doc.add_heading("8.2 Volume Persistente", level=2) add_table_with_header( doc, ["Volume", "Container", "Date Stocate"], [ ["sites", "erpnext", "Configurari site, fisiere uploadate, backup-uri"], ["mariadb-data", "mariadb", "Baza de date completa ERPNext"], ], ) doc.add_heading("8.3 Nginx Reverse Proxy", level=2) p = doc.add_paragraph() p.add_run( "Un server Nginx actioneaza ca reverse proxy in fata tuturor serviciilor, oferind:" ) add_bullet_list(doc, [ "Terminare TLS/SSL cu certificate Let's Encrypt", "Rutare trafic catre serviciile corespunzatoare pe baza path-ului URL", "Compresie gzip pentru optimizarea transferului", "Buffering si caching la nivel de proxy", "Rate limiting si protectie DDoS de baza", ]) doc.add_heading("8.4 Backup Automat Zilnic", level=2) p = doc.add_paragraph() p.add_run( "Sistemul de backup include:" ) add_bullet_list(doc, [ "Backup automat zilnic al bazei de date MariaDB (via ERPNext scheduler)", "Backup incrementali ale fisierelor uploadate", "Retentie: 7 backup-uri zilnice, 4 saptamanale, 3 lunare", "Stocare offsite pe server extern pentru protectie la dezastre", "Testare periodica a restaurarii din backup", ]) doc.add_page_break() # ========================================================================= # CHAPTER 9: Cerinte Non-Functionale Indeplinite # ========================================================================= doc.add_heading("9. Cerinte Non-Functionale Indeplinite", level=1) doc.add_heading("9.1 Performanta", level=2) p = doc.add_paragraph() p.add_run( "Website-ul DiDi a fost optimizat pentru performanta si a obtinut urmatoarele " "scoruri in testele Google Lighthouse:" ) add_table_with_header( doc, ["Metrica", "Scor / Valoare", "Obiectiv", "Status"], [ ["Lighthouse Performance", "78/100", "> 70/100", "Indeplinit"], ["First Contentful Paint (FCP)", "0.8s", "< 1.8s", "Indeplinit"], ["Largest Contentful Paint (LCP)", "1.2s", "< 2.5s", "Indeplinit"], ["Best Practices", "100/100", "> 90/100", "Indeplinit"], ["Time to Interactive (TTI)", "1.5s", "< 3.8s", "Indeplinit"], ], ) doc.add_heading("9.2 Accesibilitate", level=2) add_table_with_header( doc, ["Metrica", "Scor / Valoare", "Standard", "Status"], [ ["Lighthouse Accessibility", "91/100", "WCAG 2.1 AA", "Indeplinit"], ["Contrast culori", "Conform", "WCAG 2.1 AA (4.5:1)", "Indeplinit"], ["Navigare keyboard", "Complet", "WCAG 2.1 AA", "Indeplinit"], ["Alt text imagini", "Complet", "WCAG 2.1 AA", "Indeplinit"], ["ARIA labels", "Complet", "WCAG 2.1 AA", "Indeplinit"], ], ) doc.add_heading("9.3 SEO", level=2) add_table_with_header( doc, ["Metrica", "Scor / Valoare", "Status"], [ ["Lighthouse SEO", "100/100", "Indeplinit"], ["sitemap.xml", "Generat automat", "Indeplinit"], ["robots.txt", "Configurat", "Indeplinit"], ["Meta tags (title, description)", "Unice per pagina", "Indeplinit"], ["Open Graph tags", "Complete", "Indeplinit"], ["Structured data", "Schema.org", "Indeplinit"], ["URL-uri semantice", "Curate si descriptive", "Indeplinit"], ], ) doc.add_heading("9.4 Securitate", level=2) add_table_with_header( doc, ["Metrica", "Valoare", "Status"], [ ["npm audit vulnerabilitati", "0 vulnerabilitati", "Indeplinit"], ["HTTPS/TLS", "Certificate valide pe toate endpoint-urile", "Indeplinit"], ["Headers securitate", "CSP, HSTS, X-Frame-Options, X-Content-Type-Options", "Indeplinit"], ["Cookie flags", "HttpOnly, Secure, SameSite=Lax", "Indeplinit"], ["Input validation", "Server-side validation pe toate API routes", "Indeplinit"], ["SQL Injection", "Protectie prin ORM (Frappe/ERPNext)", "Indeplinit"], ["XSS", "Protectie prin React auto-escaping + CSP", "Indeplinit"], ], ) doc.add_heading("9.5 Sumar Lighthouse", level=2) p = doc.add_paragraph() p.add_run( "Rezultatele complete ale auditului Google Lighthouse pentru pagina principala " "a website-ului DiDi confirma indeplinirea tuturor cerintelor non-functionale " "stabilite in caietul de sarcini al proiectului PNRR:" ) add_table_with_header( doc, ["Categorie Lighthouse", "Scor", "Calificativ"], [ ["Performance", "78/100", "Bun"], ["Accessibility", "91/100", "Excelent"], ["Best Practices", "100/100", "Perfect"], ["SEO", "100/100", "Perfect"], ], ) # Save document output_path = os.path.join(OUTPUT_DIR, "Arhitectura_Detaliata.docx") doc.save(output_path) print(f" -> Salvat: {output_path}") return output_path # ============================================================================== # MAIN # ============================================================================== if __name__ == "__main__": print("=" * 70) print(" Generator Documente Livrabile PNRR - Platforma DiDi") print(f" TOP CLOSSERS SRL | CUI: {COMPANY['cui']}") print(f" Proiect: {COMPANY['project_ref']}") print("=" * 70) print() os.makedirs(OUTPUT_DIR, exist_ok=True) results = [] try: results.append(generate_manual_utilizare()) except Exception as e: print(f" EROARE Document 1: {e}") import traceback traceback.print_exc() try: results.append(generate_ghid_erpnext()) except Exception as e: print(f" EROARE Document 2: {e}") import traceback traceback.print_exc() try: results.append(generate_documentatie_api()) except Exception as e: print(f" EROARE Document 3: {e}") import traceback traceback.print_exc() try: results.append(generate_arhitectura()) except Exception as e: print(f" EROARE Document 4: {e}") import traceback traceback.print_exc() print() print("=" * 70) print(f" Finalizat! {len(results)} documente generate cu succes.") print("=" * 70) for r in results: print(f" - {r}") print()