feat: send accepted teamer invoices to configurable email inbox
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\EventListener;
|
||||
|
||||
use App\Email\Mailer;
|
||||
use App\Entity\Disposition;
|
||||
use App\Entity\Teamer;
|
||||
use App\Entity\Upload;
|
||||
use App\Event\DocumentConfirmedEvent;
|
||||
use App\EventListener\DatevEmailSubscriber;
|
||||
use App\Model\EmailPathAttachmentDto;
|
||||
use App\Service\Upload\UploadHandler;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class DatevEmailSubscriberTest extends TestCase
|
||||
{
|
||||
private Mailer&MockObject $mailer;
|
||||
private UploadHandler&MockObject $uploadHandler;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
private DatevEmailSubscriber $subscriber;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->mailer = $this->createMock(Mailer::class);
|
||||
$this->uploadHandler = $this->createMock(UploadHandler::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->subscriber = new DatevEmailSubscriber(
|
||||
$this->mailer,
|
||||
$this->uploadHandler,
|
||||
$this->logger,
|
||||
'[email protected]',
|
||||
'[email protected]',
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetSubscribedEvents(): void
|
||||
{
|
||||
$events = DatevEmailSubscriber::getSubscribedEvents();
|
||||
|
||||
$this->assertArrayHasKey(DocumentConfirmedEvent::NAME, $events);
|
||||
$this->assertSame('onDocumentConfirmed', $events[DocumentConfirmedEvent::NAME]);
|
||||
}
|
||||
|
||||
public function testSkipsNonInvoiceDocuments(): void
|
||||
{
|
||||
$document = $this->createMock(Upload::class);
|
||||
$document->method('getType')->willReturn(Upload::TYPE_CONTRACT);
|
||||
|
||||
$this->mailer
|
||||
->expects($this->never())
|
||||
->method('createAndSendEmail');
|
||||
|
||||
$event = new DocumentConfirmedEvent($document);
|
||||
$this->subscriber->onDocumentConfirmed($event);
|
||||
}
|
||||
|
||||
public function testSkipsDocumentWithoutDisposition(): void
|
||||
{
|
||||
$document = $this->createMock(Upload::class);
|
||||
$document->method('getType')->willReturn(Upload::TYPE_INVOICE);
|
||||
$document->method('getDisposition')->willReturn(null);
|
||||
$document->method('getId')->willReturn(1);
|
||||
|
||||
$this->logger
|
||||
->expects($this->once())
|
||||
->method('warning')
|
||||
->with('DATEV email skipped: document has no disposition', ['document' => 1]);
|
||||
|
||||
$this->mailer
|
||||
->expects($this->never())
|
||||
->method('createAndSendEmail');
|
||||
|
||||
$event = new DocumentConfirmedEvent($document);
|
||||
$this->subscriber->onDocumentConfirmed($event);
|
||||
}
|
||||
|
||||
public function testSkipsDispositionWithoutTeamer(): void
|
||||
{
|
||||
$disposition = $this->createMock(Disposition::class);
|
||||
$disposition->method('getTeamer')->willReturn(null);
|
||||
|
||||
$document = $this->createMock(Upload::class);
|
||||
$document->method('getType')->willReturn(Upload::TYPE_INVOICE);
|
||||
$document->method('getDisposition')->willReturn($disposition);
|
||||
$document->method('getId')->willReturn(2);
|
||||
|
||||
$this->logger
|
||||
->expects($this->once())
|
||||
->method('warning')
|
||||
->with('DATEV email skipped: disposition has no teamer', ['document' => 2]);
|
||||
|
||||
$this->mailer
|
||||
->expects($this->never())
|
||||
->method('createAndSendEmail');
|
||||
|
||||
$event = new DocumentConfirmedEvent($document);
|
||||
$this->subscriber->onDocumentConfirmed($event);
|
||||
}
|
||||
|
||||
public function testSkipsMissingAttachmentFile(): void
|
||||
{
|
||||
$teamer = $this->createMock(Teamer::class);
|
||||
$teamer->method('getFullName')->willReturn('Doe, John');
|
||||
|
||||
$disposition = $this->createMock(Disposition::class);
|
||||
$disposition->method('getTeamer')->willReturn($teamer);
|
||||
|
||||
$document = $this->createMock(Upload::class);
|
||||
$document->method('getType')->willReturn(Upload::TYPE_INVOICE);
|
||||
$document->method('getDisposition')->willReturn($disposition);
|
||||
$document->method('getId')->willReturn(3);
|
||||
|
||||
$nonExistentPath = '/tmp/non_existent_file_'.uniqid().'.pdf';
|
||||
|
||||
$this->uploadHandler
|
||||
->method('getUploadFilepath')
|
||||
->with($document)
|
||||
->willReturn($nonExistentPath);
|
||||
|
||||
$this->logger
|
||||
->expects($this->once())
|
||||
->method('error')
|
||||
->with('DATEV email skipped: attachment file not found', [
|
||||
'document' => 3,
|
||||
'path' => $nonExistentPath,
|
||||
]);
|
||||
|
||||
$this->mailer
|
||||
->expects($this->never())
|
||||
->method('createAndSendEmail');
|
||||
|
||||
$event = new DocumentConfirmedEvent($document);
|
||||
$this->subscriber->onDocumentConfirmed($event);
|
||||
}
|
||||
|
||||
public function testSendsEmailWithAttachment(): void
|
||||
{
|
||||
$teamer = $this->createMock(Teamer::class);
|
||||
$teamer->method('getFullName')->willReturn('Doe, John');
|
||||
$teamer->method('__toString')->willReturn('Doe, John');
|
||||
|
||||
$disposition = $this->createMock(Disposition::class);
|
||||
$disposition->method('getTeamer')->willReturn($teamer);
|
||||
|
||||
$document = $this->createMock(Upload::class);
|
||||
$document->method('getDisposition')->willReturn($disposition);
|
||||
$document->method('getType')->willReturn(Upload::TYPE_INVOICE);
|
||||
$document->method('getId')->willReturn(42);
|
||||
$document->method('getOriginalFilename')->willReturn('original.pdf');
|
||||
$document->method('getCreatedAt')->willReturn(new \DateTimeImmutable('2026-01-15'));
|
||||
$document->method('getFilename')->willReturn('abc123.pdf');
|
||||
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'datev_test_');
|
||||
|
||||
try {
|
||||
$this->uploadHandler
|
||||
->expects($this->once())
|
||||
->method('getUploadFilepath')
|
||||
->with($document)
|
||||
->willReturn($tmpFile);
|
||||
|
||||
$this->mailer
|
||||
->expects($this->once())
|
||||
->method('createAndSendEmail')
|
||||
->willReturnCallback(function (array $context, array $options): void {
|
||||
$this->assertSame('[email protected]', $options['to']);
|
||||
$this->assertSame('[email protected]', $options['from']);
|
||||
$this->assertSame('email/datev_invoice.html.twig', $options['template']);
|
||||
$this->assertStringContainsString('Honorarnote', $options['subject']);
|
||||
|
||||
$this->assertArrayHasKey('attachments', $options);
|
||||
$this->assertCount(1, $options['attachments']);
|
||||
|
||||
$attachment = $options['attachments'][0];
|
||||
$this->assertInstanceOf(EmailPathAttachmentDto::class, $attachment);
|
||||
$this->assertSame('hn-42-doe_john-20260115.pdf', $attachment->getName());
|
||||
});
|
||||
|
||||
$event = new DocumentConfirmedEvent($document);
|
||||
$this->subscriber->onDocumentConfirmed($event);
|
||||
} finally {
|
||||
unlink($tmpFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Model;
|
||||
|
||||
use App\Model\EmailAttachmentDto;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
|
||||
class EmailAttachmentDtoTest extends TestCase
|
||||
{
|
||||
public function testAttachToAddsContentAttachment(): void
|
||||
{
|
||||
$dto = new EmailAttachmentDto('invoice.pdf', 'pdf-content', 'application/pdf');
|
||||
$email = new TemplatedEmail();
|
||||
|
||||
$dto->attachTo($email);
|
||||
|
||||
$attachments = $email->getAttachments();
|
||||
$this->assertCount(1, $attachments);
|
||||
|
||||
$body = $attachments[0]->getBody();
|
||||
$this->assertSame('pdf-content', $body);
|
||||
$this->assertSame('invoice.pdf', $attachments[0]->getFilename());
|
||||
$this->assertSame('application/pdf', $attachments[0]->getMediaType().'/'.$attachments[0]->getMediaSubtype());
|
||||
}
|
||||
|
||||
public function testGettersReturnConstructorValues(): void
|
||||
{
|
||||
$dto = new EmailAttachmentDto('test.csv', 'csv-data', 'text/csv');
|
||||
|
||||
$this->assertSame('test.csv', $dto->getName());
|
||||
$this->assertSame('csv-data', $dto->getContent());
|
||||
$this->assertSame('text/csv', $dto->getMimeType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Model;
|
||||
|
||||
use App\Model\EmailPathAttachmentDto;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||
|
||||
class EmailPathAttachmentDtoTest extends TestCase
|
||||
{
|
||||
public function testAttachToAddsPathAttachment(): void
|
||||
{
|
||||
$tmpFile = tempnam(sys_get_temp_dir(), 'test_attachment_');
|
||||
file_put_contents($tmpFile, 'dummy-pdf-content');
|
||||
|
||||
try {
|
||||
$dto = new EmailPathAttachmentDto($tmpFile, 'invoice.pdf');
|
||||
$email = new TemplatedEmail();
|
||||
|
||||
$dto->attachTo($email);
|
||||
|
||||
$attachments = $email->getAttachments();
|
||||
$this->assertCount(1, $attachments);
|
||||
$this->assertSame('invoice.pdf', $attachments[0]->getFilename());
|
||||
$this->assertSame('application/pdf', $attachments[0]->getMediaType().'/'.$attachments[0]->getMediaSubtype());
|
||||
} finally {
|
||||
unlink($tmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
public function testDefaultMimeTypeIsApplicationPdf(): void
|
||||
{
|
||||
$dto = new EmailPathAttachmentDto('/tmp/test.pdf', 'test.pdf');
|
||||
|
||||
$this->assertSame('application/pdf', $dto->getMimeType());
|
||||
}
|
||||
|
||||
public function testCustomMimeType(): void
|
||||
{
|
||||
$dto = new EmailPathAttachmentDto('/tmp/test.csv', 'test.csv', 'text/csv');
|
||||
|
||||
$this->assertSame('text/csv', $dto->getMimeType());
|
||||
}
|
||||
|
||||
public function testGettersReturnConstructorValues(): void
|
||||
{
|
||||
$dto = new EmailPathAttachmentDto('/var/uploads/file.pdf', 'document.pdf', 'application/pdf');
|
||||
|
||||
$this->assertSame('/var/uploads/file.pdf', $dto->getPath());
|
||||
$this->assertSame('document.pdf', $dto->getName());
|
||||
$this->assertSame('application/pdf', $dto->getMimeType());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user