ediSchema = Edi\Message::loadSchema('CLOCKT'); if (!$this->ediSchema) throw new Exception('Can not load EDI schema.'); $this->restrictToSenders = $db->getValue( "SELECT restrictToSenders FROM exchangeConfig LIMIT 1"); $this->paramsRes = $db->query( "SELECT `code`, `name`, `subname`, `position`, `type`, `required` FROM `param`" ); $res = $db->query( "SELECT COLUMN_NAME columnName FROM information_schema.`COLUMNS` WHERE TABLE_NAME = 'ekt' AND TABLE_SCHEMA = SCHEMA()" ); $this->columns = []; while ($row = $res->fetch_object()) $this->columns[$row->columnName] = true; $inbox = imap_search($this->imap, 'ALL'); if ($inbox) { foreach ($inbox as $msg) $this->loadMail($db, $msg); $inboxCount = count($inbox); if ($inboxCount > 0) echo "Total $inboxCount messages processed.\n"; } } function loadMail($db, $msg) { $imap = $this->imap; // Gets EKT messages from email try { $msgStructure = imap_fetchstructure($imap, $msg); $result = []; // Gets the mail sender and Message-ID $header = imap_headerinfo($imap, $msg); $from = $header->from; if (property_exists($header, 'message_id')) $messageId = trim($header->message_id, '<>'); else $messageId = NULL; if ($from && count($from) > 0) $sender = $from[0]->mailbox .'@'. $from[0]->host; else $sender = NULL; if ($this->restrictToSenders) { $isAllowed = $db->getValue( "SELECT COUNT(*) > 0 FROM mailSender WHERE mail = #", [$sender] ); if (!$isAllowed) throw new Exception('Mail processing from unknown senders is disabled'); } $db->query('CALL mail_new(#, #, @mailFk)', [$messageId, $sender]); $mailId = $db->getValue("SELECT @mailFk"); echo "Message from: $sender\n"; echo " -> Message id: $messageId\n"; // Searches the EDI message on mail parts $matchTypes = [TYPEAPPLICATION, TYPETEXT]; $this->imapFindParts($msgStructure, $matchTypes, [], $result); $count = 0; $error = NULL; foreach ($result as $msgSection) try { $part = imap_bodystruct($imap, $msg, $msgSection); $ediString = imap_fetchbody($imap, $msg, $msgSection); switch ($part->encoding) { case ENCBASE64: $ediString = imap_base64($ediString); break; case ENCQUOTEDPRINTABLE: $ediString = imap_qprint($ediString); break; } if (!Edi\Message::isEdiString($ediString)) continue; $db->update('mail', ['source' => $ediString], ['id' => $mailId] ); // Creates the EDI object and loads its exchanges $ediMessage = new Edi\Message(); $ediMessage->parse($ediString, $this->ediSchema); $db->startTransaction(); $unb = $ediMessage->section; $unhs = $unb->childs['UNH']; foreach ($unhs as $unh) foreach ($lins = $unh->childs['LIN'] as $lin) { $this->processMessage($db, $unh, $lin, $mailId); $count++; } $db->commit(); } catch (Exception $e) { $db->rollback(); throw $e; } if ($count == 0) throw new Exception('No part with EDI format was found'); echo " -> Mail id: $mailId\n"; echo " -> Loaded exchanges: $count\n"; $folder = $this->imapConf['successFolder']; $db->update('mail', ['nExchanges' => $count], ['id' => $mailId] ); } catch (Exception $e) { $error = $e->getMessage(); error_log($error); $folder = $this->imapConf['errorFolder']; $db->update('mail', ['error' => $error], ['id' => $mailId] ); } // Moves the mail to another folder $folder = sprintf('%s', $folder); if (!imap_mail_move($imap, $msg, $folder)) error_log('Can\'t move message to %s: %s' ,$folder ,imap_last_error() ); } function processMessage($db, $unh, $lin, $mailId) { $ediValues = ['mailId' => $mailId]; // Gets the exchange params $this->paramsRes->data_seek(0); while ($row = $this->paramsRes->fetch_object()) { switch ($row->type) { case 'INTEGER': $type = Type::INTEGER; break; case 'DOUBLE': $type = Type::DOUBLE; break; case 'DATE': $type = Type::DATE; break; case 'TIME': $type = Type::TIME; break; default: $type = Type::STRING; } $value = $lin->getValue( $row->name, $row->position, $type, $row->subname); if (!isset($value) && $row->required) throw new Exception('Missing required parameter: '. $row->code); $ediValues[$row->code] = $value; } // Gets the exchange features $res = $db->query( 'SELECT presentation_order, feature FROM item_feature WHERE item_id = #ref AND entry_date <= CURDATE() AND(expiry_date IS NULL OR expiry_date >= CURDATE()) GROUP BY presentation_order' ,$ediValues ); if ($res) while ($row = $res->fetch_object()) { $value = $lin->getValue('IMD', 2, Type::INTEGER, $row->feature); $ediValues['s'.$row->presentation_order] = $value; } else throw new Exception('Can\'t get the item features.'); for ($i = 1; $i <= 6; $i++) if (!isset($ediValues['s'.$i])) $ediValues['s'.$i] = NULL; // Adds the exchange to the Database $insertValues = []; foreach ($ediValues as $code => $value) if (isset($this->columns[$code]) && !empty($value)) $insertValues[$code] = $value; $deliveryNumber = nullIf($ediValues, 'deliveryNumber'); $fec = nullIf($ediValues, 'fec'); $year = isset($fec) ? $fec->format('Y') : null; $insertValues['entryYear'] = $year; $isNew = false; $update = false; try { $db->insert('ekt', $insertValues); $ektFk = $db->lastInsertId(); $isNew = true; } catch (Exception $e) { if ($e->getCode() == 1062) $update = true; else throw $e; } if ($update && isset($year) && isset($deliveryNumber)) { $ektFk = $db->getValue( "SELECT id FROM ekt WHERE deliveryNumber = # AND entryYear = #", [$deliveryNumber, $year] ); $canUpdate = $ektFk && $db->getValue( "SELECT COUNT(*) = 0 FROM ekt t JOIN `exchange` b ON b.ektFk = t.id JOIN exchangeConfig c WHERE t.id = # AND b.typeFk != c.presaleFk", [$ektFk] ); if ($canUpdate) { $db->update('ekt', $insertValues, ['id' => $ektFk] ); } } $db->call('ekt_refresh', [$ektFk, $mailId]); try { if ($isNew) $db->call('ekt_load', [$ektFk]); } catch (Exception $e) { error_log("CALL ekt_load($ektFk): {$e->getMessage()}"); } $db->insert('exchange', [ 'mailFk' => $mailId, 'typeFk' => $ediValues['bgm'], 'ektFk' => $ektFk ]); } function imapFindParts(&$part, &$matchTypes, $section, &$result) { if (in_array($part->type, $matchTypes)) { if (count($section) > 0) $result[] = implode('.', $section); else $result[] = '1'; } elseif ($part->type == TYPEMULTIPART) foreach ($part->parts as $i => $subpart) { array_push($section, $i + 1); $this->imapFindParts($subpart, $matchTypes, $section, $result); array_pop($section); } } }