update
This commit is contained in:
336
.paul/phases/15-email-sending/15-01-PLAN.md
Normal file
336
.paul/phases/15-email-sending/15-01-PLAN.md
Normal file
@@ -0,0 +1,336 @@
|
||||
---
|
||||
phase: 15-email-sending
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- composer.json
|
||||
- composer.lock
|
||||
- src/Modules/Email/EmailSendingService.php
|
||||
- src/Modules/Email/VariableResolver.php
|
||||
- src/Modules/Email/AttachmentGenerator.php
|
||||
- src/Modules/Orders/OrdersController.php
|
||||
- resources/views/orders/show.php
|
||||
- resources/scss/modules/_email-send.scss
|
||||
- resources/views/orders/partials/email-send-modal.php
|
||||
- routes/web.php
|
||||
- DOCS/DB_SCHEMA.md
|
||||
- DOCS/ARCHITECTURE.md
|
||||
- DOCS/TECH_CHANGELOG.md
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Zaimplementować pełny flow wysyłki e-mail z widoku zamówienia: wybór szablonu, podgląd z rozwiązanymi zmiennymi, wysyłka przez SMTP (PHPMailer), automatyczne załączniki (paragon PDF), logowanie do email_logs.
|
||||
|
||||
## Purpose
|
||||
Zamyka milestone v0.4 Moduł E-mail — sprzedawca może wysyłać maile do kupujących bezpośrednio z zamówienia, z gotowymi szablonami i załącznikami, bez opuszczania panelu.
|
||||
|
||||
## Output
|
||||
- PHPMailer jako zależność composer
|
||||
- EmailSendingService: resolwer zmiennych, generowanie załączników, wysyłka SMTP
|
||||
- Modal wysyłki e-mail na widoku zamówienia (wybór szablonu, podgląd, wysyłka)
|
||||
- Endpoint POST /orders/{id}/send-email
|
||||
- Logowanie wysyłek w email_logs z wyświetleniem w zakładce Dokumenty
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/13-email-mailboxes/13-01-SUMMARY.md — DB foundation, SMTP mailbox CRUD, IntegrationSecretCipher
|
||||
@.paul/phases/14-email-templates/14-01-SUMMARY.md — Template CRUD, Quill.js, VARIABLE_GROUPS, ATTACHMENT_TYPES
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Settings/EmailMailboxRepository.php
|
||||
@src/Modules/Settings/EmailTemplateRepository.php
|
||||
@src/Modules/Settings/EmailTemplateController.php — VARIABLE_GROUPS, SAMPLE_DATA, ATTACHMENT_TYPES
|
||||
@src/Modules/Settings/IntegrationSecretCipher.php
|
||||
@src/Modules/Orders/OrdersController.php
|
||||
@src/Modules/Accounting/ReceiptController.php — pdf() method, dompdf pattern
|
||||
@src/Modules/Accounting/ReceiptRepository.php
|
||||
@resources/views/orders/show.php
|
||||
@routes/web.php
|
||||
@database/migrations/20260315_000056_create_email_logs_table.sql
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills (from SPECIAL-FLOWS.md)
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] sonar-scanner loaded (run after APPLY)
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Wysyłka e-mail z zamówienia
|
||||
```gherkin
|
||||
Given użytkownik jest na widoku zamówienia z adresem e-mail kupującego
|
||||
When kliknie "Wyślij e-mail", wybierze szablon i kliknie "Wyślij"
|
||||
Then e-mail zostanie wysłany na adres kupującego przez skonfigurowaną skrzynkę SMTP
|
||||
And zmienne w szablonie ({{zamowienie.numer}}, {{kupujacy.imie_nazwisko}} itd.) zostaną zastąpione danymi zamówienia
|
||||
```
|
||||
|
||||
## AC-2: Załącznik paragon PDF
|
||||
```gherkin
|
||||
Given szablon ma ustawiony attachment_1 = 'receipt'
|
||||
And zamówienie ma wystawiony paragon
|
||||
When użytkownik wyśle e-mail z tym szablonem
|
||||
Then do maila zostanie dołączony paragon w formacie PDF
|
||||
And nazwa pliku to numer_paragonu.pdf
|
||||
```
|
||||
|
||||
## AC-3: Logowanie wysyłek
|
||||
```gherkin
|
||||
Given użytkownik wysłał e-mail z zamówienia
|
||||
When otworzy zakładkę Dokumenty na widoku zamówienia
|
||||
Then zobaczy wpis z datą, tematem, odbiorcą, statusem (sent/failed) i opcją podglądu
|
||||
```
|
||||
|
||||
## AC-4: Podgląd przed wysyłką
|
||||
```gherkin
|
||||
Given użytkownik wybrał szablon w modalu wysyłki
|
||||
When kliknie "Podgląd"
|
||||
Then zobaczy temat i treść z rozwiązanymi zmiennymi (dane z aktualnego zamówienia)
|
||||
And zobaczy informację o załącznikach (jeśli dostępne)
|
||||
```
|
||||
|
||||
## AC-5: Walidacja i obsługa błędów
|
||||
```gherkin
|
||||
Given brak skonfigurowanej skrzynki SMTP lub brak aktywnych szablonów
|
||||
When użytkownik kliknie "Wyślij e-mail"
|
||||
Then zobaczy komunikat o braku konfiguracji (nie modal wysyłki)
|
||||
|
||||
Given wysyłka SMTP nie powiedzie się
|
||||
When błąd zostanie zwrócony przez PHPMailer
|
||||
Then email_logs zapisze status='failed' z error_message
|
||||
And użytkownik zobaczy komunikat o błędzie
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: PHPMailer + EmailSendingService z resolwerem zmiennych i załącznikami</name>
|
||||
<files>
|
||||
composer.json, composer.lock,
|
||||
src/Modules/Email/EmailSendingService.php,
|
||||
src/Modules/Email/VariableResolver.php,
|
||||
src/Modules/Email/AttachmentGenerator.php
|
||||
</files>
|
||||
<action>
|
||||
1. `composer require phpmailer/phpmailer` (v6.x)
|
||||
|
||||
2. Utworzyć `src/Modules/Email/VariableResolver.php`:
|
||||
- Metoda `resolve(string $template, array $orderData): string`
|
||||
- Zamienia `{{grupa.zmienna}}` na wartości z danych zamówienia
|
||||
- Mapowanie zmiennych (reuse logiki z EmailTemplateController::VARIABLE_GROUPS):
|
||||
- zamowienie: numer (external_id lub id), numer_zewnetrzny, zrodlo (source), kwota (total_amount), waluta (currency), data (created_at formatted)
|
||||
- kupujacy: imie_nazwisko, email, telefon, login — z buyer_* pól zamówienia
|
||||
- adres: ulica, miasto, kod_pocztowy, kraj — z delivery address
|
||||
- firma: nazwa, nip — z company_settings
|
||||
- Zmienne bez wartości → pusty string (nie zostawiać {{...}})
|
||||
- Metoda `buildVariableMap(array $order, array $companySettings): array` — buduje płaską mapę 'grupa.zmienna' => wartość
|
||||
|
||||
3. Utworzyć `src/Modules/Email/AttachmentGenerator.php`:
|
||||
- Metoda `generate(string $type, array $order): ?array` → ['filename' => '...', 'content' => '...binary...', 'mime' => 'application/pdf']
|
||||
- Dla type='receipt':
|
||||
- Pobrać paragon z ReceiptRepository::findByOrderId($orderId)
|
||||
- Jeśli brak paragonu → return null (nie blokuje wysyłki, po prostu brak załącznika)
|
||||
- Renderować PDF tym samym wzorcem co ReceiptController::pdf() — Dompdf z widoku receipts/print
|
||||
- Filename: str_replace(['/', '\\'], '_', $receiptNumber) . '.pdf'
|
||||
- Rozszerzalność: switch($type) z case 'receipt', default → null
|
||||
|
||||
4. Utworzyć `src/Modules/Email/EmailSendingService.php`:
|
||||
- Konstruktor: Medoo $db, VariableResolver, AttachmentGenerator, IntegrationSecretCipher, TemplateEngine
|
||||
- Metoda `send(int $orderId, int $templateId, ?int $mailboxId = null): array`
|
||||
- Pobrać zamówienie (z buyer, address, items)
|
||||
- Pobrać szablon z EmailTemplateRepository
|
||||
- Pobrać skrzynkę: jeśli mailboxId podany → użyj; jeśli szablon ma mailbox_id → użyj; fallback → domyślna (is_default=1)
|
||||
- Jeśli brak skrzynki → throw RuntimeException('Brak skonfigurowanej skrzynki SMTP')
|
||||
- Pobrać company_settings dla zmiennych firma.*
|
||||
- Rozwiązać zmienne w subject i body_html przez VariableResolver
|
||||
- Wygenerować załączniki przez AttachmentGenerator (jeśli attachment_1 set)
|
||||
- Wysłać przez PHPMailer:
|
||||
- SMTP auth z odszyfrowanym hasłem (IntegrationSecretCipher::decrypt)
|
||||
- SMTPSecure = mailbox.smtp_encryption (tls/ssl/none→'')
|
||||
- Port = mailbox.smtp_port
|
||||
- From = mailbox.sender_email / sender_name
|
||||
- To = order buyer email
|
||||
- Subject = resolved subject
|
||||
- Body = resolved body_html (isHTML = true)
|
||||
- Attachments: addStringAttachment() dla każdego wygenerowanego
|
||||
- Zalogować do email_logs: template_id, mailbox_id, order_id, recipient_email, recipient_name, subject, body_html (resolved), attachments_json, status (sent/failed), error_message, sent_at
|
||||
- Return: ['success' => bool, 'error' => ?string, 'log_id' => int]
|
||||
- Metoda `preview(int $orderId, int $templateId): array`
|
||||
- Rozwiązuje zmienne i zwraca ['subject' => '...', 'body_html' => '...', 'attachments' => [...nazwy]]
|
||||
- NIE wysyła maila
|
||||
|
||||
Avoid:
|
||||
- Nie używać natywnej funkcji mail() — tylko PHPMailer SMTP
|
||||
- Nie duplikować logiki zmiennych z EmailTemplateController — wydzielić do VariableResolver i docelowo zastąpić SAMPLE_DATA w kontrolerze referencją
|
||||
- Nie tworzyć plików tymczasowych na dysku dla załączników — używać addStringAttachment (in-memory)
|
||||
</action>
|
||||
<verify>
|
||||
- composer show phpmailer/phpmailer zwraca wersję 6.x
|
||||
- php -l src/Modules/Email/EmailSendingService.php — brak błędów składni
|
||||
- php -l src/Modules/Email/VariableResolver.php — brak błędów składni
|
||||
- php -l src/Modules/Email/AttachmentGenerator.php — brak błędów składni
|
||||
</verify>
|
||||
<done>AC-1 (wysyłka SMTP), AC-2 (załącznik paragon), AC-5 (obsługa błędów) — backend ready</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Modal wysyłki e-mail + endpoint + logowanie + wyświetlanie w Dokumentach</name>
|
||||
<files>
|
||||
src/Modules/Orders/OrdersController.php,
|
||||
resources/views/orders/show.php,
|
||||
resources/views/orders/partials/email-send-modal.php,
|
||||
resources/scss/modules/_email-send.scss,
|
||||
routes/web.php,
|
||||
DOCS/DB_SCHEMA.md,
|
||||
DOCS/ARCHITECTURE.md,
|
||||
DOCS/TECH_CHANGELOG.md
|
||||
</files>
|
||||
<action>
|
||||
1. Dodać route w routes/web.php:
|
||||
- POST /orders/{id}/send-email → [$ordersController, 'sendEmail']
|
||||
- POST /orders/{id}/email-preview → [$ordersController, 'emailPreview']
|
||||
|
||||
2. W OrdersController dodać metody:
|
||||
- `sendEmail(Request $request)`:
|
||||
- CSRF validation (_token)
|
||||
- Pobrać template_id, mailbox_id (opcjonalny) z POST
|
||||
- Wywołać EmailSendingService::send()
|
||||
- Flash message sukces/błąd
|
||||
- JSON response (AJAX): ['success' => bool, 'message' => '...']
|
||||
- `emailPreview(Request $request)`:
|
||||
- Pobrać template_id z POST
|
||||
- Wywołać EmailSendingService::preview()
|
||||
- JSON response: ['subject' => '...', 'body_html' => '...', 'attachments' => [...]]
|
||||
|
||||
3. Utworzyć `resources/views/orders/partials/email-send-modal.php`:
|
||||
- Modal overlay z klasą .email-send-modal
|
||||
- Formularz:
|
||||
- Select "Szablon" — lista aktywnych szablonów (przekazane z kontrolera)
|
||||
- Select "Skrzynka" — lista aktywnych skrzynek (z opcją "Domyślna z szablonu")
|
||||
- Readonly "Odbiorca" — buyer email z zamówienia
|
||||
- Przycisk "Podgląd" — AJAX POST /orders/{id}/email-preview → wyświetla resolved subject + body w div.preview
|
||||
- Div .email-preview-area — ukryty domyślnie, pokazuje podgląd subject + body + lista załączników
|
||||
- Przycisk "Wyślij" — AJAX POST /orders/{id}/send-email
|
||||
- Przycisk "Anuluj" — zamyka modal
|
||||
- CSRF token hidden input
|
||||
- JS: fetch API do podglądu i wysyłki, loading states, error handling
|
||||
- Po udanej wysyłce: zamknij modal, pokaż OrderProAlerts.success(), odśwież sekcję dokumentów (lub reload)
|
||||
|
||||
4. W resources/views/orders/show.php:
|
||||
- Dodać przycisk "Wyślij e-mail" w .order-details-actions (po "Wystaw paragon"):
|
||||
- `<button class="btn btn--secondary" id="btn-send-email">Wyślij e-mail</button>`
|
||||
- Przycisk aktywny tylko jeśli: są aktywne szablony + aktywne skrzynki + zamówienie ma buyer email
|
||||
- Jeśli brak konfiguracji → btn--disabled z title="Skonfiguruj skrzynkę i szablony w Ustawieniach"
|
||||
- Include modal partial: `<?php include __DIR__ . '/partials/email-send-modal.php'; ?>`
|
||||
- W zakładce "documents" dodać sekcję "Wysłane e-maile":
|
||||
- Tabela: Data | Temat | Odbiorca | Status | Akcje
|
||||
- Status: badge sent (zielony) / failed (czerwony)
|
||||
- Akcje: przycisk "Podgląd" otwierający modal z body_html z logu
|
||||
- Dane z email_logs WHERE order_id = current order
|
||||
- W kontrolerze show(): załadować email_logs dla zamówienia + aktywne szablony + aktywne skrzynki
|
||||
|
||||
5. Style SCSS w resources/scss/modules/_email-send.scss:
|
||||
- .email-send-modal — overlay + centered card (wzorować na istniejących modalach w projekcie)
|
||||
- .email-preview-area — border, padding, max-height z overflow-y scroll
|
||||
- .email-log-status--sent / --failed badges
|
||||
- Import w głównym SCSS
|
||||
|
||||
6. Zaktualizować DOCS/DB_SCHEMA.md, DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md:
|
||||
- Nowy moduł Email (EmailSendingService, VariableResolver, AttachmentGenerator)
|
||||
- Nowe endpointy /orders/{id}/send-email i /email-preview
|
||||
- PHPMailer jako nowa zależność
|
||||
|
||||
Avoid:
|
||||
- Nie dodawać natywnych alert()/confirm() — używać OrderProAlerts
|
||||
- Nie trzymać styli w pliku widoku — tylko SCSS
|
||||
- Nie tworzyć nowego taba na widoku zamówienia — wyświetlać emaile w istniejącej zakładce Dokumenty
|
||||
- Nie dodawać bulk email z listy zamówień — tylko z widoku pojedynczego zamówienia
|
||||
</action>
|
||||
<verify>
|
||||
- php -l src/Modules/Orders/OrdersController.php — brak błędów składni
|
||||
- Przycisk "Wyślij e-mail" widoczny na /orders/{id}
|
||||
- Modal otwiera się po kliknięciu
|
||||
- Podgląd pokazuje rozwiązane zmienne
|
||||
- Po wysyłce email_logs ma nowy rekord
|
||||
- Zakładka Dokumenty pokazuje historię wysyłek
|
||||
</verify>
|
||||
<done>AC-1 (pełny flow UI), AC-3 (logowanie i wyświetlanie), AC-4 (podgląd), AC-5 (walidacja UI) satisfied</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Pełny flow wysyłki e-mail z widoku zamówienia: wybór szablonu, podgląd ze zmiennymi, wysyłka SMTP z załącznikiem paragon PDF, logowanie i historia w zakładce Dokumenty.</what-built>
|
||||
<how-to-verify>
|
||||
1. Otwórz zamówienie z adresem e-mail kupującego: /orders/{id}
|
||||
2. Sprawdź przycisk "Wyślij e-mail" w pasku akcji
|
||||
3. Kliknij — powinien otworzyć się modal z wyborem szablonu i skrzynki
|
||||
4. Wybierz szablon → kliknij "Podgląd" → sprawdź czy zmienne zostały rozwiązane (numer zamówienia, dane kupującego)
|
||||
5. Jeśli szablon ma załącznik "Paragon" i zamówienie ma paragon → podgląd powinien pokazać załącznik
|
||||
6. Kliknij "Wyślij" → sprawdź czy mail dotarł na skrzynkę odbiorcy
|
||||
7. Sprawdź zakładkę Dokumenty → sekcja "Wysłane e-maile" powinna pokazywać nowy wpis ze statusem "sent"
|
||||
8. Przetestuj błąd: zmień SMTP na nieprawidłowy → wyślij → status powinien być "failed" z error_message
|
||||
9. Przetestuj brak konfiguracji: usuń skrzynki → przycisk powinien być nieaktywny z tooltipem
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- database/migrations/* — tabele email_logs, email_mailboxes, email_templates już istnieją (Phase 13-14)
|
||||
- src/Modules/Settings/EmailMailboxController.php — CRUD skrzynek bez zmian
|
||||
- src/Modules/Settings/EmailTemplateController.php — CRUD szablonów bez zmian (poza ewentualnym reuse VARIABLE_GROUPS)
|
||||
- src/Modules/Accounting/* — moduł paragonów bez zmian (tylko odczyt z ReceiptRepository)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Brak bulk email z listy zamówień — tylko z widoku pojedynczego zamówienia
|
||||
- Brak kolejki/retry — wysyłka synchroniczna (async/cron to przyszły milestone)
|
||||
- Brak edycji treści maila w modalu — tylko wybór szablonu i podgląd
|
||||
- Brak nowych tabel/migracji — wykorzystanie istniejącej email_logs
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] composer show phpmailer/phpmailer zwraca 6.x
|
||||
- [ ] php -l na wszystkich nowych/zmodyfikowanych plikach PHP — brak błędów
|
||||
- [ ] Przycisk "Wyślij e-mail" widoczny na widoku zamówienia
|
||||
- [ ] Modal otwiera się, podgląd rozwiązuje zmienne
|
||||
- [ ] Wysyłka SMTP działa (mail dociera do odbiorcy)
|
||||
- [ ] Załącznik paragon PDF dołączany gdy szablon ma attachment_1='receipt'
|
||||
- [ ] email_logs zapisuje wpis z poprawnym statusem
|
||||
- [ ] Zakładka Dokumenty wyświetla historię emaili
|
||||
- [ ] DOCS zaktualizowane
|
||||
- [ ] Wszystkie acceptance criteria spełnione
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All tasks completed
|
||||
- All verification checks pass
|
||||
- No PHP errors or warnings introduced
|
||||
- E-mail z zamówienia działa end-to-end (szablon → podgląd → wysyłka → log → historia)
|
||||
- Paragon PDF jako załącznik działa (gdy dostępny)
|
||||
- Brak regresji w istniejących modułach (zamówienia, paragony, szablony, skrzynki)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/15-email-sending/15-01-SUMMARY.md`
|
||||
</output>
|
||||
164
.paul/phases/15-email-sending/15-01-SUMMARY.md
Normal file
164
.paul/phases/15-email-sending/15-01-SUMMARY.md
Normal file
@@ -0,0 +1,164 @@
|
||||
---
|
||||
phase: 15-email-sending
|
||||
plan: 01
|
||||
subsystem: email
|
||||
tags: [phpmailer, smtp, dompdf, quill, activity-log]
|
||||
|
||||
requires:
|
||||
- phase: 13-email-mailboxes
|
||||
provides: email_mailboxes table, SMTP credentials, IntegrationSecretCipher
|
||||
- phase: 14-email-templates
|
||||
provides: email_templates table, Quill.js editor, VARIABLE_GROUPS, ATTACHMENT_TYPES
|
||||
|
||||
provides:
|
||||
- EmailSendingService (send + preview via SMTP)
|
||||
- VariableResolver (template variable substitution)
|
||||
- AttachmentGenerator (receipt PDF in-memory)
|
||||
- Send email modal on order detail view
|
||||
- Activity log integration for email events
|
||||
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: [phpmailer/phpmailer v7.0.2]
|
||||
patterns: [activity-log-based email history, in-memory PDF attachments]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- src/Modules/Email/EmailSendingService.php
|
||||
- src/Modules/Email/VariableResolver.php
|
||||
- src/Modules/Email/AttachmentGenerator.php
|
||||
- resources/views/orders/partials/email-send-modal.php
|
||||
- resources/scss/modules/_email-send.scss
|
||||
modified:
|
||||
- src/Modules/Orders/OrdersController.php
|
||||
- routes/web.php
|
||||
- resources/views/orders/show.php
|
||||
- resources/lang/pl.php
|
||||
|
||||
key-decisions:
|
||||
- "PHPMailer v7.0.2 jako SMTP transport (nie natywny mail())"
|
||||
- "Email history jako wpisy w order_activity_log (nie osobna sekcja UI)"
|
||||
- "VariableResolver wydzielony jako osobna klasa (reuse poza kontrolerem szablonow)"
|
||||
- "Zalaczniki in-memory (addStringAttachment) bez plikow tymczasowych"
|
||||
|
||||
patterns-established:
|
||||
- "Activity log integration: nowe typy zdarzen (email_sent/email_failed) z tlumaczeniami w pl.php"
|
||||
- "Email modul (App\\Modules\\Email) jako oddzielny namespace od Settings"
|
||||
|
||||
duration: ~90min
|
||||
started: 2026-03-17T10:00:00Z
|
||||
completed: 2026-03-17T11:30:00Z
|
||||
---
|
||||
|
||||
# Phase 15 Plan 01: Wysylka e-mail z zamowien — Summary
|
||||
|
||||
**Pelny flow wysylki e-mail z widoku zamowienia: wybor szablonu, podglad ze zmiennymi, wysylka SMTP z zalacznikiem paragon PDF, logowanie w historii zamowienia.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~90min |
|
||||
| Tasks | 2 auto + 1 checkpoint |
|
||||
| Files created | 5 |
|
||||
| Files modified | 9 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Wysylka e-mail z zamowienia | Pass | Szablon + zmienne + SMTP wysylka dziala end-to-end |
|
||||
| AC-2: Zalacznik paragon PDF | Pass | Dompdf generuje PDF in-memory, dolaczany przez addStringAttachment |
|
||||
| AC-3: Logowanie wysylek | Pass | Wpis w email_logs + order_activity_log (widoczny w zakladce Historia) |
|
||||
| AC-4: Podglad przed wysylka | Pass | AJAX preview z rozwiazanymi zmiennymi + lista zalacznikow |
|
||||
| AC-5: Walidacja i obsluga bledow | Pass | Brak konfiguracji → btn disabled; blad SMTP → status failed + komunikat |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Modul `App\Modules\Email` z 3 klasami: EmailSendingService, VariableResolver, AttachmentGenerator
|
||||
- Modal wysylki na widoku zamowienia z wyborem szablonu, skrzynki, podgladem i wysylka AJAX
|
||||
- Integracja z order_activity_log — wysylka maila pojawia sie jako zdarzenie w historii zamowienia
|
||||
- PHPMailer v7.0.2 jako zaleznosc composer
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Email/EmailSendingService.php` | Created | Glowna klasa wysylki: send(), preview(), SMTP transport, logowanie |
|
||||
| `src/Modules/Email/VariableResolver.php` | Created | Zamiana {{grupa.zmienna}} na dane zamowienia/kupujacego/firmy |
|
||||
| `src/Modules/Email/AttachmentGenerator.php` | Created | Generowanie PDF paragonu in-memory przez dompdf |
|
||||
| `resources/views/orders/partials/email-send-modal.php` | Created | Modal: wybor szablonu/skrzynki, podglad, wysylka AJAX |
|
||||
| `resources/scss/modules/_email-send.scss` | Created | Style modala i podgladu |
|
||||
| `src/Modules/Orders/OrdersController.php` | Modified | Dodano sendEmail(), emailPreview(), email deps w konstruktorze |
|
||||
| `routes/web.php` | Modified | Nowe route'y POST send-email/email-preview, wiring EmailSendingService |
|
||||
| `resources/views/orders/show.php` | Modified | Przycisk "Wyslij e-mail", include modala |
|
||||
| `resources/lang/pl.php` | Modified | Tlumaczenia email_sent, email_failed |
|
||||
| `resources/scss/app.scss` | Modified | Import modules/email-send |
|
||||
| `composer.json` | Modified | phpmailer/phpmailer v7.0.2 |
|
||||
| `DOCS/DB_SCHEMA.md` | Modified | Wpis o PHPMailer i module Email |
|
||||
| `DOCS/ARCHITECTURE.md` | Modified | Nowy modul + route'y |
|
||||
| `DOCS/TECH_CHANGELOG.md` | Modified | Changelog Phase 15 |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| PHPMailer v7.0.2 (nie natywny mail()) | Pelna kontrola SMTP, auth, TLS, zalaczniki | Nowa zaleznosc composer |
|
||||
| Email history w activity_log (nie osobna sekcja) | Spojnosc UX — jeden timeline zdarzen | Prostszy widok, mniej kodu |
|
||||
| VariableResolver jako osobna klasa | Reuse logiki zmiennych poza kontrolerem szablonow | Czystsza architektura |
|
||||
| In-memory PDF (addStringAttachment) | Brak plikow tymczasowych na dysku | Prostsze, bezpieczniejsze |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope change | 1 | Email history przeniesiona z Dokumentow do activity_log |
|
||||
| Auto-fixed | 3 | Bugfixy odkryte podczas pracy |
|
||||
|
||||
**Total impact:** Istotna zmiana UX (lepsza), plus naprawione 3 bugi.
|
||||
|
||||
### Scope Change
|
||||
|
||||
**1. Email history w activity_log zamiast osobnej sekcji w Dokumentach**
|
||||
- **Zmiana:** User zażądał przeniesienia historii e-maili z zakładki Dokumenty do zakładki Historia jako wpisy w activity_log
|
||||
- **Wpływ:** Usunięto loadEmailLogs(), emailLogsList z widoku; dodano recordActivity() w EmailSendingService
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Search duplicate :search parameter (OrdersRepository)**
|
||||
- **Found during:** Testowanie UI
|
||||
- **Issue:** PDO named parameter `:search` użyty 5x w jednym zapytaniu — wyszukiwanie po nazwisku klienta nie działało
|
||||
- **Fix:** Osobne nazwy parametrów `:s1` do `:s5`
|
||||
- **Files:** `src/Modules/Orders/OrdersRepository.php`
|
||||
|
||||
**2. Migration idempotency (attachment_1)**
|
||||
- **Found during:** Uruchomienie migracji
|
||||
- **Issue:** `ALTER TABLE ADD COLUMN attachment_1` nie miała warunku IF NOT EXISTS
|
||||
- **Fix:** Dodano sprawdzenie information_schema + PREPARE/EXECUTE
|
||||
- **Files:** `database/migrations/20260316_000001_add_attachment1_to_email_templates.sql`
|
||||
|
||||
**3. ReceiptController actor name**
|
||||
- **Found during:** Review activity_log entries
|
||||
- **Issue:** Używał `$user['username']` (nieistniejące pole) zamiast `$user['name']`
|
||||
- **Fix:** Zmieniono na `$user['name'] ?? $user['email']`
|
||||
- **Files:** `src/Modules/Accounting/ReceiptController.php`
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Milestone v0.4 Modul E-mail kompletny (Phase 13 + 14 + 15)
|
||||
- Pelny flow: skrzynki SMTP → szablony z Quill.js → wysylka z zamowien
|
||||
|
||||
**Concerns:**
|
||||
- Brak kolejki/retry dla nieudanych wysylek (synchroniczna wysylka)
|
||||
- Brak bulk email z listy zamowien
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 15-email-sending, Plan: 01*
|
||||
*Completed: 2026-03-17*
|
||||
Reference in New Issue
Block a user