Posts by SRieger

    --- a/classes/Modules/SystemMailer/SystemMailer.php

    +++ b/classes/Modules/SystemMailer/SystemMailer.php

    @@ -195,4 +195,23 @@



    return $email;


    }


    -}


    +


    + /**


    + * Wandelt alle Texte in der EmailMessage zu UTF-8 um (Betreff, Body, Absendername etc.)


    + */


    + private function convertEmailToUtf8(EmailMessage $email): EmailMessage


    + {


    + $convert = function (?string $text): ?string {


    + if ($text === null) return null;


    + $converted = mb_convert_encoding($text, 'UTF-8', 'auto');


    + return $converted !== false ? $converted : mb_convert_encoding($text, 'UTF-8', 'ISO-8859-1');


    + };


    +


    + return $email


    + ->withSubject($convert($email->getSubject()))


    + ->withBodyText($convert($email->getBodyText()))


    + ->withBodyHtml($convert($email->getBodyHtml()))


    + ->withSenderName($convert($email->getSenderName()));


    + }


    +


    +}

    UTF-8-Fix für fehlerhafte Umlaute in versendeten Mails (CRM / SystemMailer.php)

    Die Datei SystemMailer.php unter

    /classes/Modules/SystemMailer/

    enthält standardmäßig keine UTF-8-Kodierung – das führt zu falschen Zeichen (z. B. ü statt ü) im Sent-Ordner oder bei bestimmten Mailclients.

    🛠 Lösung:

    In dieser modifizierten Version (SystemMailer_UTF8_FIXED.php) wird der Betreff, Text, HTML-Body und Absendername automatisch auf UTF-8 konvertiert.

    👉 Funktioniert analog zum Ticket-Modul, wo die Darstellung korrekt ist.

    Einfach austauschen oder als Basis für Patch verwenden.

    (Bitte vor dem Patch prüfen, ob das mit der letzten Version der Datei übereinstimmt. Meine auf der NAS war von 2024.


    <?php


    declare(strict_types=1);


    namespace Xentral\Modules\SystemMailer;


    use Exception;

    use Xentral\Components\Logger\LoggerAwareTrait;

    use Xentral\Components\Mailer\Data\EmailMessage;

    use Xentral\Components\Mailer\Data\FileAttachment;

    use Xentral\Components\Mailer\Exception\MailerTransportException;

    use Xentral\Components\Mailer\Mailer;

    use Xentral\Modules\GoogleApi\Exception\AuthorizationExpiredException;

    use Xentral\Modules\SystemMailer\Data\EmailBackupAccount;

    use Xentral\Modules\SystemMailer\Service\EmailAccountGateway;

    use Xentral\Modules\SystemMailer\Service\MailBodyCleaner;

    use Xentral\Modules\SystemMailer\Service\MailerTransportFactory;

    use Xentral\Modules\SystemMailer\Service\MailLogService;


    final class SystemMailer

    {

    use LoggerAwareTrait;


    /** @var MailerTransportFactory $factory */

    private $factory;


    /** @var MailLogService $emailLog */

    private $emailLog;


    /** @var MailBodyCleaner $cleaner */

    private $cleaner;


    /** @var EmailAccountGateway $accountGateway */

    private $accountGateway;


    /**

    * @param MailerTransportFactory $factory

    * @param EmailAccountGateway $accountGateway

    * @param MailLogService $emailLog

    * @param MailBodyCleaner $cleaner

    */

    public function __construct(

    MailerTransportFactory $factory,

    EmailAccountGateway $accountGateway,

    MailLogService $emailLog,

    MailBodyCleaner $cleaner

    ) {

    $this->factory = $factory;

    $this->emailLog = $emailLog;

    $this->cleaner = $cleaner;

    $this->accountGateway = $accountGateway;

    }


    /**

    * @param EmailMessage $email

    * @param EmailBackupAccount $account

    *

    * @return bool success

    */

    public function send(EmailMessage $email, EmailBackupAccount $account, &$mailerror_text): bool

    {

    // 🛠 UTF-8 Charset Fix (sofern EmailMessage keinen eigenen Setter hat, muss man Inhalte vorher konvertieren)

    $email = $this->convertEmailToUtf8($email);


    $transport = $this->factory->createMailerTransport($account);

    $email = $this->cleaner->cleanEmailBody($email);

    $mailer = new Mailer($transport);

    try {

    $success = $mailer->send($email);

    $this->emailLog->logOutgoingMail($email, $account, $transport->getStatus());

    if ($transport->hasErrors()) {

    $errors = ['error' => $transport->getErrorMessages()];

    $this->logger->error('Error while sending email.', $errors);

    $mailerror_text = 'Error while sending email. '.implode(', ',$errors['error']);

    }

    return $success;

    } catch (AuthorizationExpiredException $e) {

    $this->logger->error('Error while sending email. Google authorization expired', ['error' => $e->getMessage()]);

    $mailerror_text = 'Google authorization expired: '.$e->getMessage();

    } catch (MailerTransportException $e) {

    $this->logger->error('Error while sending email.', ['error' => $e->getMessage()]);

    $mailerror_text = 'Transport error: '.$e->getMessage();

    } catch (Exception $e) {

    $this->logger->error('General mail error', ['error' => $e->getMessage()]);

    $mailerror_text = 'General error: '.$e->getMessage();

    }


    return false;

    }

    } catch (MailerTransportException $e) {

    $this->logger->error($e->getMessage(), ['dump' => $transport->getErrorMessages()]);


    $errors = ['error' => $transport->getErrorMessages()];

    $this->logger->error('Error while sending email.', $errors);

    $mailerror_text = 'Error while sending email. '.implode(', ',$errors['error']);


    return false;

    } catch (AuthorizationExpiredException $e) {

    $this->logger->error(

    'Error while sending email. Google authorization expired',

    ['exception' => $e]

    );

    $mailerror_text = 'Error while sending email. Google authorization expired';

    return false;

    }


    return $success;

    }


    /**

    * @deprecated only for backwards compatibility with erpApi::MailSendFinal

    *

    * @param string $senderEmail

    * @param string $senderName

    * @param array $recipients

    * @param string $subject

    * @param string $body

    * @param array $attachFiles

    * @param array $ccs

    * @param array $bccs

    *

    * @return bool success

    */

    public function composeAndSendEmail(

    $senderEmail,

    $senderName,

    $recipients,

    $subject,

    $body,

    $attachFiles = [],

    $ccs = [],

    $bccs = [],

    &$mailerror_text

    :( bool {

    //cannot use Mailer component if no emailbackup account exists

    $account = null;


    try {

    $account = $this->accountGateway->getAccountByEmail($senderEmail);

    } catch (Exception $e) {

    $account = null;

    $this->logger->error($e->getMessage(), ['exception' => $e]);

    $mailerror_text = 'Error while sending email: Account not found: '.$senderEmail;

    }


    if ($account === null) {

    return false;

    }


    //use Mailer component only for 'smtp' or 'gmail' authtype

    if (

    $account->isSmtpEnabled() === false

    || (

    $account->getSmtpAuthType() !== EmailBackupAccount::AUTH_SMTP

    && $account->getSmtpAuthType() !== EmailBackupAccount::AUTH_GMAIL

    )

    ) {

    $mailerror_text = 'Authtype error.';

    return false;

    }


    $email = new EmailMessage($subject, $body, $recipients, $ccs, $bccs);

    foreach ($attachFiles as $file) {

    $this->logger->debug("Attaching file", ['filename' => $file]);

    if ($file !== null && file_exists($file)) {

    $email->addAttachment(new FileAttachment($file));

    } else {

    $this->logger->debug("Attaching file failed", ['filename' => $file]);

    }

    }


    $account = $account

    ->withSmtpSenderName($senderName)

    ->withDisplayName($senderName);


    //send email with new Mailer component


    $mailerror_text = "";


    $result = $this->send($email, $account, $mailerror_text);


    return ($result);

    }


    /**

    * @deprecated only for backwards compatibility with erpApi::MailSendFinal

    *

    * @param array $recipients

    * @param string $subject

    * @param string $body

    * @param array $attachFiles

    * @param array $ccs

    * @param array $bccs

    *

    * @return EmailMessage

    */

    public function composeEmail(

    $recipients,

    $subject,

    $body,

    $attachFiles = [],

    $ccs = [],

    $bccs = []

    :( EmailMessage {

    $email = new EmailMessage($subject, $body, $recipients, $ccs, $bccs);

    foreach ($attachFiles as $file) {

    if ($file !== null && file_exists($file)) {

    $email->addAttachment(new FileAttachment($file));

    }

    }


    return $email;

    }


    /**

    * Wandelt alle Texte in der EmailMessage zu UTF-8 um (Betreff, Body, Absendername etc.)

    */

    private function convertEmailToUtf8(EmailMessage $email): EmailMessage

    {

    $convert = function (?string $text): ?string {

    if ($text === null) {

    return null;

    }

    $converted = mb_convert_encoding($text, 'UTF-8', 'auto');

    if ($converted === false) {

    $converted = mb_convert_encoding($text, 'UTF-8', 'ISO-8859-1');

    }

    return $converted;

    };


    $email = $email->withSubject($convert($email->getSubject()));

    $email = $email->withBodyText($convert($email->getBodyText()));

    $email = $email->withBodyHtml($convert($email->getBodyHtml()));

    $email = $email->withSenderName($convert($email->getSenderName()));


    return $email;

    }


    }

    public function encodeToUtf8(string $string): string

    {

    $converted = mb_convert_encoding(

    $string,

    'UTF-8',

    'auto'

    );


    // Fallback

    if ($converted === false) {

    $converted = mb_convert_encoding(

    $string,

    'UTF-8',

    'iso-8859-1'

    );

    }


    return ($converted);

    }


    => Ist in Datei TicketService.php unter classes/Modules/Ticke/Service
    E-Mails im Ticketsystem werdne versendet


    Dump:

    Leider gibt es keine Rubrik für Beschaffung und Einkauf.

    Daher hier das Thema


    Weil es kein Anfragemodul gibt, mache ich Anfragen immer als Bestellung im Entwurfsmodus und sende das pdf als nicht freigegebene Bestellung mit dem Hinweis Anfrage als Ticket raus.

    Nun habe ich einge Entwürfe als Bestellung im System und gerade ist mir aufgefallen, dass nur immer nur eine Seite angezeigt und somit man nicht auf die restlichen Bestellentwürfe zugreifen kann.

    Ausser man erweitert oben die Liste auf mehrere Einträge pro Seite


    Bis auf die Buchhaltung funktioniert es.
    In der Bestellung auf Einlagern klicken.

    Dann im Wareneingang die Positionen bestätigen.


    Und in der Verbindlichkeit Freigabe Einkauf


    Bei der Buchhaltung habe ich die automatische Kontierung noch nicht implementiert.

    Daher vermutlich Fehler.


    Früher habe ich in der Bestellung nur auf Bestellung abschliessen gedückt. vermutlich was das dann nicht korrekt bei Dienstleistungen.