orders = $orders; $this->productRepo = $productRepo; $this->settingsRepo = $settingsRepo; $this->transportRepo = $transportRepo; } public function details(int $orderId): array { return $this->orders->findForAdmin($orderId); } public function statuses(): array { return $this->orders->orderStatuses(); } /** * @return array{names: array, colors: array} */ public function statusData(): array { return $this->orders->orderStatusData(); } /** * @return array{items: array>, total: int} */ public function listForAdmin( array $filters, string $sortColumn = 'date_order', string $sortDir = 'DESC', int $page = 1, int $perPage = 15 ): array { return $this->orders->listForAdmin($filters, $sortColumn, $sortDir, $page, $perPage); } public function nextOrderId(int $orderId): ?int { return $this->orders->nextOrderId($orderId); } public function prevOrderId(int $orderId): ?int { return $this->orders->prevOrderId($orderId); } public function saveNotes(int $orderId, string $notes): bool { return $this->orders->saveNotes($orderId, $notes); } public function saveOrderByAdmin(array $input): bool { $saved = $this->orders->saveOrderByAdmin( (int)($input['order_id'] ?? 0), (string)($input['client_name'] ?? ''), (string)($input['client_surname'] ?? ''), (string)($input['client_street'] ?? ''), (string)($input['client_postal_code'] ?? ''), (string)($input['client_city'] ?? ''), (string)($input['client_email'] ?? ''), (string)($input['firm_name'] ?? ''), (string)($input['firm_street'] ?? ''), (string)($input['firm_postal_code'] ?? ''), (string)($input['firm_city'] ?? ''), (string)($input['firm_nip'] ?? ''), (int)($input['transport_id'] ?? 0), (string)($input['inpost_paczkomat'] ?? ''), (int)($input['payment_method_id'] ?? 0) ); return $saved; } // ========================================================================= // Order products management (admin) // ========================================================================= public function searchProducts(string $query, string $langId): array { if (!$this->productRepo || trim($query) === '') { return []; } $rows = $this->productRepo->searchProductByNameAjax($query, $langId); $results = []; foreach ($rows as $row) { $productId = (int)($row['product_id'] ?? 0); if ($productId <= 0) { continue; } $product = $this->productRepo->findCached($productId, $langId); if (!is_array($product)) { continue; } $name = isset($product['language']['name']) ? (string)$product['language']['name'] : ''; $img = $this->productRepo->getProductImg($productId); $results[] = [ 'product_id' => $productId, 'parent_product_id' => (int)($product['parent_id'] ?? 0), 'name' => $name, 'sku' => (string)($product['sku'] ?? ''), 'ean' => (string)($product['ean'] ?? ''), 'price_brutto' => (float)($product['price_brutto'] ?? 0), 'price_brutto_promo' => (float)($product['price_brutto_promo'] ?? 0), 'vat' => (float)($product['vat'] ?? 0), 'quantity' => (int)($product['quantity'] ?? 0), 'image' => $img, ]; } return $results; } public function saveOrderProducts(int $orderId, array $productsData): bool { if ($orderId <= 0) { return false; } $currentProducts = $this->orders->orderProducts($orderId); $currentById = []; foreach ($currentProducts as $cp) { $currentById[(int)$cp['id']] = $cp; } $submittedIds = []; foreach ($productsData as $item) { $orderProductId = (int)($item['order_product_id'] ?? 0); $deleted = !empty($item['delete']); if ($deleted && $orderProductId > 0) { // Usunięcie — zwrot na stan $existing = isset($currentById[$orderProductId]) ? $currentById[$orderProductId] : null; if ($existing) { $this->adjustStock((int)$existing['product_id'], (int)$existing['quantity']); } $this->orders->deleteOrderProduct($orderProductId); $submittedIds[] = $orderProductId; continue; } if ($deleted) { continue; } if ($orderProductId > 0 && isset($currentById[$orderProductId])) { // Istniejący produkt — aktualizacja $existing = $currentById[$orderProductId]; $newQty = max(1, (int)($item['quantity'] ?? 1)); $oldQty = (int)$existing['quantity']; $qtyDiff = $oldQty - $newQty; $update = [ 'quantity' => $newQty, 'price_brutto' => (float)($item['price_brutto'] ?? $existing['price_brutto']), 'price_brutto_promo' => (float)($item['price_brutto_promo'] ?? $existing['price_brutto_promo']), ]; $this->orders->updateOrderProduct($orderProductId, $update); // Korekta stanu: qtyDiff > 0 = zmniejszono ilość = zwrot na stan if ($qtyDiff !== 0) { $this->adjustStock((int)$existing['product_id'], $qtyDiff); } $submittedIds[] = $orderProductId; } elseif ($orderProductId === 0) { // Nowy produkt $productId = (int)($item['product_id'] ?? 0); $qty = max(1, (int)($item['quantity'] ?? 1)); $this->orders->addOrderProduct($orderId, [ 'product_id' => $productId, 'parent_product_id' => (int)($item['parent_product_id'] ?? $productId), 'name' => (string)($item['name'] ?? ''), 'attributes' => '', 'vat' => (float)($item['vat'] ?? 0), 'price_brutto' => (float)($item['price_brutto'] ?? 0), 'price_brutto_promo' => (float)($item['price_brutto_promo'] ?? 0), 'quantity' => $qty, 'message' => '', 'custom_fields' => '', ]); // Zmniejsz stan magazynowy $this->adjustStock($productId, -$qty); } } // Usunięte z formularza (nie przesłane) — zwrot na stan foreach ($currentById as $cpId => $cp) { if (!in_array($cpId, $submittedIds)) { $this->adjustStock((int)$cp['product_id'], (int)$cp['quantity']); $this->orders->deleteOrderProduct($cpId); } } // Przelicz koszt dostawy (próg darmowej dostawy) $this->recalculateTransportCost($orderId); return true; } public function getFreeDeliveryThreshold(): float { if (!$this->settingsRepo) { return 0.0; } return (float)$this->settingsRepo->getSingleValue('free_delivery'); } private function adjustStock(int $productId, int $delta): void { if (!$this->productRepo || $productId <= 0 || $delta === 0) { return; } $currentQty = $this->productRepo->getQuantity($productId); if ($currentQty === null) { return; } $newQty = max(0, $currentQty + $delta); $this->productRepo->updateQuantity($productId, $newQty); } private function recalculateTransportCost(int $orderId): void { $order = $this->orders->findRawById($orderId); if (!$order) { return; } $transportId = (int)($order['transport_id'] ?? 0); if ($transportId <= 0 || !$this->transportRepo || !$this->settingsRepo) { return; } $transport = $this->transportRepo->findActiveById($transportId); if (!is_array($transport)) { return; } // Oblicz sumę produktów (bez dostawy) $productsSummary = $this->calculateProductsTotal($orderId); $freeDelivery = (float)$this->settingsRepo->getSingleValue('free_delivery'); if ((int)($transport['delivery_free'] ?? 0) === 1 && $freeDelivery > 0 && $productsSummary >= $freeDelivery) { $transportCost = 0.0; } else { $transportCost = (float)($transport['cost'] ?? 0); } $this->orders->updateTransportCost($orderId, $transportCost); } private function calculateProductsTotal(int $orderId): float { $products = $this->orders->orderProducts($orderId); $summary = 0.0; foreach ($products as $row) { $pricePromo = (float)($row['price_brutto_promo'] ?? 0); $price = (float)($row['price_brutto'] ?? 0); $quantity = (float)($row['quantity'] ?? 0); if ($pricePromo > 0) { $summary += $pricePromo * $quantity; } else { $summary += $price * $quantity; } } return $summary; } public function changeStatus(int $orderId, int $status, bool $sendEmail): array { $order = $this->orders->findRawById($orderId); if (!$order || (int)$order['status'] === $status) { return ['result' => false]; } $db = $this->orders->getDb(); if ($this->orders->updateOrderStatus($orderId, $status)) { $this->orders->insertStatusHistory($orderId, $status, $sendEmail ? 1 : 0); $response = ['result' => true]; if ($sendEmail) { $order['status'] = $status; $response['email'] = $this->sendStatusChangeEmail($order); } // Apilo status sync $this->syncApiloStatusIfNeeded($order, $status); return $response; } return ['result' => false]; } public function resendConfirmationEmail(int $orderId): bool { global $settings; $db = $this->orders->getDb(); $order = $this->orders->orderDetailsFrontend($orderId); if (!$order || !$order['id']) { return false; } $coupon = (int)$order['coupon_id'] ? (new \Domain\Coupon\CouponRepository($db))->find((int)$order['coupon_id']) : null; $mail_order = \Shared\Tpl\Tpl::view('shop-order/mail-summary', [ 'settings' => $settings, 'order' => $order, 'coupon' => $coupon, ]); $settings['ssl'] ? $base = 'https' : $base = 'http'; $regex = "-(]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i"; $mail_order = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $mail_order); $regex = "-(]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i"; $mail_order = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $mail_order); \Shared\Helpers\Helpers::send_email($order['client_email'], \Shared\Helpers\Helpers::lang('potwierdzenie-zamowienia-ze-sklepu') . ' ' . $settings['firm_name'], $mail_order); \Shared\Helpers\Helpers::send_email($settings['contact_email'], 'Nowe zamówienie / ' . $settings['firm_name'] . ' / ' . $order['number'] . ' - ' . $order['client_surname'] . ' ' . $order['client_name'], $mail_order); return true; } public function setOrderAsUnpaid(int $orderId): bool { $this->orders->setAsUnpaid($orderId); return true; } public function setOrderAsPaid(int $orderId, bool $sendMail): bool { $order = $this->orders->findRawById($orderId); if (!$order) { return false; } // Apilo payment sync $this->syncApiloPaymentIfNeeded($order); // Mark as paid $this->orders->setAsPaid($orderId); // Set status to 1 (opłacone) without email $this->changeStatus($orderId, 1, false); // Set status to 4 (przyjęte do realizacji) with email $this->changeStatus($orderId, 4, $sendMail); return true; } public function sendOrderToApilo(int $orderId): bool { global $mdb; if ($orderId <= 0) { return false; } $order = $this->orders->findForAdmin($orderId); if (empty($order) || empty($order['apilo_order_id'])) { return false; } $integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb ); $accessToken = $integrationsRepository -> apiloGetAccessToken(); if (!$accessToken) { return false; } $newStatus = 8; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://projectpro.apilo.com/rest/api/orders/' . $order['apilo_order_id'] . '/status/'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'id' => (int)$order['apilo_order_id'], 'status' => (int)( new \Domain\ShopStatus\ShopStatusRepository($mdb) )->getApiloStatusId( (int)$newStatus ), ])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $accessToken, 'Accept: application/json', 'Content-Type: application/json', ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $apiloResultRaw = curl_exec($ch); $apiloResult = json_decode((string)$apiloResultRaw, true); if (!is_array($apiloResult) || (int)($apiloResult['updates'] ?? 0) !== 1) { curl_close($ch); return false; } $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_orders' AND COLUMN_NAME != 'id'"; $stmt = $mdb->query($query); $columns = $stmt ? $stmt->fetchAll(\PDO::FETCH_COLUMN) : []; $columnsList = implode(', ', $columns); $mdb->query('INSERT INTO pp_shop_orders (' . $columnsList . ') SELECT ' . $columnsList . ' FROM pp_shop_orders pso WHERE pso.id = ' . $orderId); $newOrderId = (int)$mdb->id(); $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_order_products' AND COLUMN_NAME != 'id' AND COLUMN_NAME != 'order_id'"; $stmt2 = $mdb->query($query); $columns = $stmt2 ? $stmt2->fetchAll(\PDO::FETCH_COLUMN) : []; $columnsList = implode(', ', $columns); $mdb->query('INSERT INTO pp_shop_order_products (order_id, ' . $columnsList . ') SELECT ' . $newOrderId . ', ' . $columnsList . ' FROM pp_shop_order_products psop WHERE psop.order_id = ' . $orderId); $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_order_statuses' AND COLUMN_NAME != 'id' AND COLUMN_NAME != 'order_id'"; $stmt3 = $mdb->query($query); $columns = $stmt3 ? $stmt3->fetchAll(\PDO::FETCH_COLUMN) : []; $columnsList = implode(', ', $columns); $mdb->query('INSERT INTO pp_shop_order_statuses (order_id, ' . $columnsList . ') SELECT ' . $newOrderId . ', ' . $columnsList . ' FROM pp_shop_order_statuses psos WHERE psos.order_id = ' . $orderId); $mdb->delete('pp_shop_orders', ['id' => $orderId]); $mdb->delete('pp_shop_order_products', ['order_id' => $orderId]); $mdb->delete('pp_shop_order_statuses', ['order_id' => $orderId]); $mdb->update('pp_shop_orders', ['apilo_order_id' => null], ['id' => $newOrderId]); curl_close($ch); return true; } public function toggleTrustmateSend(int $orderId): array { $newValue = $this->orders->toggleTrustmateSend($orderId); if ($newValue === null) { return [ 'result' => false, ]; } return [ 'result' => true, 'trustmate_send' => $newValue, ]; } public function deleteOrder(int $orderId): bool { return $this->orders->deleteOrder($orderId); } // ========================================================================= // Apilo sync queue (migrated from \shop\Order) // ========================================================================= private const APILO_SYNC_QUEUE_FILE = '/temp/apilo-sync-queue.json'; public function processApiloSyncQueue(int $limit = 10): int { $queue = self::loadApiloSyncQueue(); if (!\Shared\Helpers\Helpers::is_array_fix($queue)) { return 0; } $processed = 0; foreach ($queue as $key => $task) { if ($processed >= $limit) { break; } $order_id = (int)($task['order_id'] ?? 0); if ($order_id <= 0) { unset($queue[$key]); continue; } $order = $this->orders->findRawById($order_id); if (!$order) { unset($queue[$key]); continue; } $error = ''; $sync_failed = false; $payment_pending = !empty($task['payment']) && (int)$order['paid'] === 1; if ($payment_pending && (int)$order['apilo_order_id']) { if (!$this->syncApiloPayment($order)) { $sync_failed = true; $error = 'payment_sync_failed'; } } $status_pending = isset($task['status']) && $task['status'] !== null && $task['status'] !== ''; if (!$sync_failed && $status_pending && (int)$order['apilo_order_id']) { if (!$this->syncApiloStatus($order, (int)$task['status'])) { $sync_failed = true; $error = 'status_sync_failed'; } } if ($sync_failed) { $task['attempts'] = (int)($task['attempts'] ?? 0) + 1; $task['last_error'] = $error; $task['updated_at'] = date('Y-m-d H:i:s'); $queue[$key] = $task; } else { unset($queue[$key]); } $processed++; } self::saveApiloSyncQueue($queue); return $processed; } // ========================================================================= // Private: email // ========================================================================= private function sendStatusChangeEmail(array $order): bool { if (!$order['client_email']) { return false; } $db = $this->orders->getDb(); $order_statuses = $this->orders->orderStatuses(); $firm_name = (new \Domain\Settings\SettingsRepository($db))->getSingleValue('firm_name'); $status = (int)$order['status']; $number = $order['number']; $subjects = [ 0 => $firm_name . ' - zamówienie [NUMER] zostało złożone', 1 => $firm_name . ' - zamówienie [NUMER] zostało opłacone', 2 => $firm_name . ' - płatność za zamówienie [NUMER] została odrzucona', 3 => $firm_name . ' - płatność za zamówienie [NUMER] jest sprawdzania ręcznie', 4 => $firm_name . ' - zamówienie [NUMER] zostało przyjęte do realizacji', 5 => $firm_name . ' - zamówienie [NUMER] zostało wysłane', 6 => $firm_name . ' - zamówienie [NUMER] zostało zrealizowane', 7 => $firm_name . ' - zamówienie [NUMER] zostało przygotowane go wysłania', 8 => $firm_name . ' - zamówienie [NUMER] zostało anulowane', ]; $subject = isset($subjects[$status]) ? str_replace('[NUMER]', $number, $subjects[$status]) : ''; if (!$subject) { return false; } $email = new \Shared\Email\Email(); $email->load_by_name('#sklep-zmiana-statusu-zamowienia'); $email->text = str_replace('[NUMER_ZAMOWIENIA]', $number, $email->text); $email->text = str_replace('[DATA_ZAMOWIENIA]', date('Y/m/d', strtotime($order['date_order'])), $email->text); $email->text = str_replace('[STATUS]', isset($order_statuses[$status]) ? $order_statuses[$status] : '', $email->text); return $email->send($order['client_email'], $subject, true); } // ========================================================================= // Private: Apilo sync // ========================================================================= private function syncApiloPaymentIfNeeded(array $order): void { global $config; $db = $this->orders->getDb(); $integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db); $apilo_settings = $integrationsRepository->getSettings('apilo'); if (!$apilo_settings['enabled'] || !$apilo_settings['access-token'] || !$apilo_settings['sync_orders']) { return; } if (isset($config['debug']['apilo']) && $config['debug']['apilo']) { self::appendApiloLog("SET AS PAID\n" . print_r($order, true)); } if ($order['apilo_order_id'] && !$this->syncApiloPayment($order)) { self::queueApiloSync((int)$order['id'], true, null, 'payment_sync_failed'); } } private function syncApiloStatusIfNeeded(array $order, int $status): void { global $config; $db = $this->orders->getDb(); $integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db); $apilo_settings = $integrationsRepository->getSettings('apilo'); if (!$apilo_settings['enabled'] || !$apilo_settings['access-token'] || !$apilo_settings['sync_orders']) { return; } if (isset($config['debug']['apilo']) && $config['debug']['apilo']) { self::appendApiloLog("UPDATE STATUS\n" . print_r($order, true)); } if ($order['apilo_order_id'] && !$this->syncApiloStatus($order, $status)) { self::queueApiloSync((int)$order['id'], false, $status, 'status_sync_failed'); } } private function syncApiloPayment(array $order): bool { global $config; $db = $this->orders->getDb(); $integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db); if (!(int)$order['apilo_order_id']) { return true; } $payment_type = (int)(new \Domain\PaymentMethod\PaymentMethodRepository($db))->getApiloPaymentTypeId((int)$order['payment_method_id']); if ($payment_type <= 0) { $payment_type = 1; } $payment_date = new \DateTime($order['date_order']); $access_token = $integrationsRepository->apiloGetAccessToken(); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $order['apilo_order_id'] . '/payment/'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'amount' => str_replace(',', '.', $order['summary']), 'paymentDate' => $payment_date->format('Y-m-d\TH:i:s\Z'), 'type' => $payment_type, ])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer " . $access_token, "Accept: application/json", "Content-Type: application/json", ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 15); $apilo_response = curl_exec($ch); $http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_errno($ch) ? curl_error($ch) : ''; curl_close($ch); if (isset($config['debug']['apilo']) && $config['debug']['apilo']) { self::appendApiloLog("PAYMENT RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_response, true)); } if ($curl_error !== '') return false; if ($http_code < 200 || $http_code >= 300) return false; return true; } private function syncApiloStatus(array $order, int $status): bool { global $config; $db = $this->orders->getDb(); $integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db); if (!(int)$order['apilo_order_id']) { return true; } $access_token = $integrationsRepository->apiloGetAccessToken(); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $order['apilo_order_id'] . '/status/'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'id' => $order['apilo_order_id'], 'status' => (int)(new \Domain\ShopStatus\ShopStatusRepository($db))->getApiloStatusId($status), ])); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer " . $access_token, "Accept: application/json", "Content-Type: application/json", ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 15); $apilo_result = curl_exec($ch); $http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_error = curl_errno($ch) ? curl_error($ch) : ''; curl_close($ch); if (isset($config['debug']['apilo']) && $config['debug']['apilo']) { self::appendApiloLog("STATUS RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_result, true)); } if ($curl_error !== '') return false; if ($http_code < 200 || $http_code >= 300) return false; return true; } // ========================================================================= // Private: Apilo sync queue file helpers // ========================================================================= private static function queueApiloSync(int $order_id, bool $payment, ?int $status, string $error): void { if ($order_id <= 0) return; $queue = self::loadApiloSyncQueue(); $key = (string)$order_id; $row = is_array($queue[$key] ?? null) ? $queue[$key] : []; $row['order_id'] = $order_id; $row['payment'] = !empty($row['payment']) || $payment ? 1 : 0; if ($status !== null) { $row['status'] = $status; } $row['attempts'] = (int)($row['attempts'] ?? 0) + 1; $row['last_error'] = $error; $row['updated_at'] = date('Y-m-d H:i:s'); $queue[$key] = $row; self::saveApiloSyncQueue($queue); } private static function apiloSyncQueuePath(): string { return dirname(__DIR__, 2) . self::APILO_SYNC_QUEUE_FILE; } private static function loadApiloSyncQueue(): array { $path = self::apiloSyncQueuePath(); if (!file_exists($path)) return []; $content = file_get_contents($path); if (!$content) return []; $decoded = json_decode($content, true); if (!is_array($decoded)) return []; return $decoded; } private static function saveApiloSyncQueue(array $queue): void { $path = self::apiloSyncQueuePath(); $dir = dirname($path); if (!is_dir($dir)) { mkdir($dir, 0777, true); } file_put_contents($path, json_encode($queue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX); } private static function appendApiloLog(string $message): void { $base = isset($_SERVER['DOCUMENT_ROOT']) && $_SERVER['DOCUMENT_ROOT'] ? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\') : dirname(__DIR__, 2); $dir = $base . '/logs'; if (!is_dir($dir)) { mkdir($dir, 0777, true); } file_put_contents( $dir . '/apilo.txt', date('Y-m-d H:i:s') . ' --- ' . $message . "\n\n", FILE_APPEND ); } }