feat(115): wystawianie faktury z zamowienia (lokalne + delegowane Fakturownia)

Phase 115 complete (vertical slice "zamowienie z NIP -> faktura PDF"):
- Task 1: InvoiceRepository + InvoiceService (dual-flow orchestrator) +
  InvoiceIssueException + FakturowniaApiClient::createInvoice + buildPdfUrl
- Task 2: InvoiceController + OrdersController::toggleInvoiceRequested +
  OrdersRepository::setInvoiceRequested + auto-import invoice_requested z
  Allegro (invoice.required) i shopPRO (5-key flexible parser) + show.php
  (toggle w zakladce Platnosci + warunkowy przycisk Wystaw fakture)
- Task 3: Lista wystawionych /settings/accounting/invoices/issued z filtrami
  + invoice_preview + invoice_pdf Dompdf template + hub link
- Task 3b (dodany): NIP lookup przez MF Biala Lista (publiczne API, bez
  rejestracji) — MfWhitelistApiClient w src/Core/Http/ + /api/nip/lookup +
  przycisk "Pobierz z GUS" w formularzu

Auto-fixes podczas smoke testu (5):
- GUS endpoint Fakturowni nie istnial (HTML 404 -> "json is not valid");
  switch na MF Biala Liste
- PHP 8.5 curl_close() deprecation wycieka HTML przed JSON; usuniete z
  MfWhitelistApiClient i FakturowniaApiClient (3 miejsca)
- Fakturownia 422 payment_to_kind_days (nieistniejace pole) -> usuniete
- Generic "error" w 422 -> parser plaskuje errors: {pole: [...]} +
  error_log z 1000 znakow raw body
- Fakturownia security odrzuca seller_*/department_id jako "create new
  department"; usuniete z payloadu (Fakturownia uzywa danych konta)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 23:34:50 +02:00
parent 6129042ff6
commit 33ee1a1cf5
28 changed files with 3228 additions and 45 deletions

View File

@@ -55,6 +55,10 @@ use App\Modules\Email\AttachmentGenerator;
use App\Modules\Email\EmailSendingService;
use App\Modules\Email\VariableResolver;
use App\Modules\Accounting\AccountingController;
use App\Core\Http\MfWhitelistApiClient;
use App\Modules\Accounting\InvoiceController;
use App\Modules\Accounting\InvoiceRepository;
use App\Modules\Accounting\InvoiceService;
use App\Modules\Accounting\ReceiptController;
use App\Modules\Accounting\ReceiptRepository;
use App\Modules\Accounting\ReceiptService;
@@ -243,6 +247,7 @@ return static function (Application $app): void {
$invoiceConfigRepository,
$fakturowniaIntegrationRepository
);
$invoiceRepository = new InvoiceRepository($app->db());
$emailMailboxRepository = new EmailMailboxRepository(
$app->db(),
new IntegrationSecretCipher((string) $app->config('app.integrations.secret', ''))
@@ -287,6 +292,14 @@ return static function (Application $app): void {
$companySettingsRepository,
new OrdersRepository($app->db())
);
$invoiceService = new InvoiceService(
$invoiceRepository,
$invoiceConfigRepository,
$companySettingsRepository,
new OrdersRepository($app->db()),
$fakturowniaIntegrationRepository,
$fakturowniaApiClient
);
$automationService = new AutomationService(
$automationRepository,
$automationExecutionLogRepository,
@@ -324,7 +337,7 @@ return static function (Application $app): void {
$allegroDeliveryMappingController
);
$printJobRepository = new PrintJobRepository($app->db());
$ordersController = new OrdersController($template, $translator, $auth, $app->orders(), $shipmentPackageRepositoryForOrders, $receiptRepository, $receiptConfigRepository, $emailSendingService, $emailTemplateRepository, $emailMailboxRepository, $app->basePath('storage'), $printJobRepository, $shopproIntegrationsRepository, $automationService);
$ordersController = new OrdersController($template, $translator, $auth, $app->orders(), $shipmentPackageRepositoryForOrders, $receiptRepository, $receiptConfigRepository, $emailSendingService, $emailTemplateRepository, $emailMailboxRepository, $app->basePath('storage'), $printJobRepository, $shopproIntegrationsRepository, $automationService, $invoiceRepository, $invoiceConfigRepository);
$ordersStatisticsController = new OrdersStatisticsController(
$template,
$translator,
@@ -349,6 +362,17 @@ return static function (Application $app): void {
$receiptRepository,
$receiptConfigRepository
);
$invoiceController = new InvoiceController(
$template,
$translator,
$auth,
$invoiceRepository,
$invoiceConfigRepository,
$companySettingsRepository,
new OrdersRepository($app->db()),
$invoiceService,
new MfWhitelistApiClient()
);
$allegroApiClient = new AllegroApiClient();
$shipmentPackageRepository = new ShipmentPackageRepository($app->db());
$shipmentService = new AllegroShipmentService(
@@ -586,6 +610,14 @@ return static function (Application $app): void {
$router->get('/orders/{id}/receipt/{receiptId}', [$receiptController, 'show'], [$authMiddleware]);
$router->get('/orders/{id}/receipt/{receiptId}/print', [$receiptController, 'printView'], [$authMiddleware]);
$router->get('/orders/{id}/receipt/{receiptId}/pdf', [$receiptController, 'pdf'], [$authMiddleware]);
// Invoices from order (Phase 115-01)
$router->post('/orders/{id}/invoice-requested/toggle', [$ordersController, 'toggleInvoiceRequested'], [$authMiddleware]);
$router->get('/orders/{id}/invoice/create', [$invoiceController, 'create'], [$authMiddleware]);
$router->post('/orders/{id}/invoice/store', [$invoiceController, 'store'], [$authMiddleware]);
$router->get('/orders/{id}/invoice/{invoiceId}', [$invoiceController, 'show'], [$authMiddleware]);
$router->get('/orders/{id}/invoice/{invoiceId}/pdf', [$invoiceController, 'pdf'], [$authMiddleware]);
$router->get('/settings/accounting/invoices/issued', [$invoiceController, 'issuedList'], [$authMiddleware]);
$router->get('/api/nip/lookup', [$invoiceController, 'nipLookup'], [$authMiddleware]);
$router->get('/orders/{id}/shipment/prepare', [$shipmentController, 'prepare'], [$authMiddleware]);
$router->post('/orders/{id}/shipment/create', [$shipmentController, 'create'], [$authMiddleware]);
$router->get('/orders/{id}/shipment/{packageId}/status', [$shipmentController, 'checkStatus'], [$authMiddleware]);