db = $db; $this->filesystemFactory = $filesystemFactory; $this->app = $app; } /** * @param string $startDate * @param string $endDate * @param string $type * * @return array */ public function getDocumentsByApi($startDate, $endDate, $type = 'inv'): array { $response = $this->prepareParametersByXentral($startDate, $endDate, $type); $response = @json_decode($response, true); if (empty($response)) { return []; } if (isset($response[0])) { return $response; } if (isset($response['rem_gs_nr']) || isset($response['inv_rech_nr'])) { return [$response]; } return []; } /** * @param string $startDate * @param string $endDate * @param string $type * * @return string */ public function prepareParametersByXentral($startDate, $endDate, $type = 'inv'): string { $this->loadConfig(); $firmKeyId = $this->config['firmkeyid']; $clientIdentifier = $this->config['clientidentifier']; if (empty($firmKeyId)) { throw new InvalidArgumentException('firmkeyid is empty'); } if (empty($clientIdentifier)) { throw new InvalidArgumentException('clientIdentifier is empty'); } if (empty($startDate)) { throw new InvalidArgumentException('startdate is empty'); } if (empty($endDate)) { throw new InvalidArgumentException('endDate is empty'); } $startDate = date('Y-m-d', strtotime($startDate)); $endDate = date('Y-m-d', strtotime($endDate)); $now = new DateTime(); $beforeTwoMonths = clone $now; $beforeTwoMonths->modify('-2 month'); if ($endDate >= $now->format('Y-m-d')) { throw new InvalidArgumentException('endDate is not in the past'); } if ($startDate < $beforeTwoMonths->format('Y-m-d')) { throw new InvalidArgumentException('endDate is older than two months'); } if ($type !== 'inv' && $type !== 'rem') { throw new InvalidArgumentException('type must be "inv" or "rem"'); } $serial = $this->app->erp->Firmendaten('lizenz'); if (empty($clientIdentifier)) { throw new InvalidArgumentException('lizenz is empty'); } $url = 'https://amazon.xentral.com/amainvoiceapp.php'; $schluessel = $this->app->erp->Firmendaten('schluessel'); if (empty($clientIdentifier)) { throw new InvalidArgumentException('schluessel is empty'); } $paras = [ 'firm_key_id' => $firmKeyId, 'client_identifier' => $clientIdentifier, 'start_date' => $startDate, 'end_date' => $endDate, 'data_type' => $type, 'json' => false, ]; $paras = [ 'serial' => $serial, 'schluessel' => $schluessel, 'paras' => json_encode($paras), ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($paras)); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $httpcode = curl_getinfo($ch); curl_close($ch); if ($response === 'Throttel is On') { $this->db->perform( "INSERT INTO `logfile` ( meldung, dump, module, action, bearbeiter, funktionsname, datum) VALUES ('Throttel is On', '', 'amainvoice', '', 'Cronjob', '', NOW() )" ); throw new ThrottlingException($response); } $httpcode = $httpcode['http_code']; if (strpos((string)$httpcode, '2') === 0) { if ($response === 'Error: Parameters not loaded [0]') { throw new AmazonInvoiceServiceException($response); } return $response; } if (!empty($response)) { $response2 = @json_decode($response, true); if (!empty($response2['error'])) { if (!empty($response2['throttled'])) { $error = $response2['error']; if (is_array($error)) { $error = reset($error); } $this->db->perform( "INSERT INTO `logfile` ( meldung, dump, module, action, bearbeiter, funktionsname, datum) VALUES ('Throttel by xentral is On', '', 'amainvoice', '', 'Cronjob', '', NOW() )" ); throw new ThrottlingException($error); } if (is_array($response2['error'])) { throw new AmazonInvoiceServiceException(reset($response2['error'])); } if (is_string($response2['error'])) { throw new AmazonInvoiceServiceException($response2['error']); } } } throw new AmazonInvoiceServiceException($response); } /** * @return array */ public function getConfig(): array { return $this->db->fetchPairs('SELECT `name`, `value` FROM `amainvoice_config`'); } /** * @param array $configArr */ public function setConfig($configArr): void { if (empty($configArr)) { throw new InvalidArgumentException('Config Array empty'); } foreach ($configArr as $key => $value) { if (empty($key)) { throw new InvalidArgumentException('Config Array invalid'); } } $config = $this->getConfig(); foreach ($configArr as $key => $value) { if (!isset($config[$key])) { $this->db->perform( 'INSERT INTO `amainvoice_config` (`name`, `value`) VALUES (:name, :value)', ['name' => $key, 'value' => (string)$value] ); } elseif ((string)$value !== $config[$key]) { $this->db->perform( 'UPDATE `amainvoice_config` SET `value` = :value WHERE `name` = :name', ['name' => $key, 'value' => (string)$value] ); } } } /** * @return array|PathInfo[] */ public function getFiles(): array { if ($this->config === null) { $this->loadConfig(); } if ($this->filesystem === null) { try { $now = new DateTime(); $beforeTwoMonths = clone $now; $beforeTwoMonths->modify('-2 month'); $startDate = new DateTime($this->config['startdate']); if ($startDate < $beforeTwoMonths) { $startDate = $beforeTwoMonths; } $toDate = clone $now; $toDate->sub(new DateInterval('P1D')); $ret = []; while ($startDate < $toDate) { $dateFormated = $startDate->format('Y-m-d'); $ret[] = new PathInfo( [ 'type' => 'file', 'filename' => $dateFormated . '.inv', 'path' => $dateFormated . '.inv', ] ); $ret[] = new PathInfo( [ 'type' => 'file', 'filename' => $dateFormated . '.rem', 'path' => $dateFormated . '.rem', ] ); $startDate->add(new DateInterval('P1D')); } return $ret; } catch (\Exception $e) { return []; } } return $this->filesystem->listFiles(''); } /** * @return bool */ public function hasConfigFileSystem(): bool { if ($this->config === null) { $this->loadConfig(); } return $this->filesystem !== null; } /** * @return array */ public function getNewFiles(): array { $files = $this->getFiles(); if (empty($files)) { return []; } $fileNames = []; foreach ($files as $file) { $fileName = $file->getPath(); $fileNames[] = $fileName; } $inDb = $this->db->fetchPairs( 'SELECT `id`, `filename` FROM `amainvoice_files` WHERE `filename` IN (:filenames)', ['filenames' => $fileNames] ); $inDb = array_values($inDb); return array_diff($fileNames, $inDb); } /** * @param string $file * @param string $to * * @return string */ public function getFile($file, $to = ''): string { try { if (empty($to)) { $to = $file; } $fileSystemConfig = [ 'permissions' => [ 'file' => [ 'public' => 0664, 'private' => 0664, ], 'dir' => [ 'public' => 0775, 'private' => 0775, ], ], ]; if (is_file($this->app->erp->GetTMP() . $to)) { return $this->app->erp->GetTMP() . $to; } $fileSystemTo = $this->filesystemFactory->createLocal($this->app->erp->GetTMP(), $fileSystemConfig); if ($fileSystemTo->write($to, $this->filesystem->read($file))) { return $this->app->erp->GetTMP() . $to; }; } catch (\Exception $e) { throw $e; } throw new InvalidArgumentException('could not download file ' . $file); } /** * @param string $type * @param string $amazonOrderId * @param string $number * @param array $datevRows * @param array $positions * @param string $pdfFile * @param bool $createOrder * * @return bool */ public function createDocument( $type, $amazonOrderId, $number, $datevRows, $positions, $pdfFile = '', $createOrder = false ): bool { if (empty($amazonOrderId)) { throw new InvalidArgumentException('amazonOrderId is empty'); } if ($number !== null && empty($number)) { throw new InvalidArgumentException('document number is empty'); } if ($type !== 'invoice' && $type !== 'returnorder') { throw new InvalidArgumentException('type not valid'); } $order = $this->db->fetchCol( "SELECT `id` FROM `auftrag` WHERE `internet` = :amazonorderid AND `internet` <> '' AND `status` <> 'storniert' ", ['amazonorderid' => $amazonOrderId] ); $firstOrderId = empty($order) ? null : reset($order); $orderCreated = false; $firstDatevRow = reset($datevRows); $isOrderFbm = true; if(!empty($firstDatevRow['fulfillmentchannel']) && $firstDatevRow['fulfillmentchannel'] === 'AFN') { $isOrderFbm = false; } $statusInfo = null; if($isOrderFbm) { $statusInfo = $this->db->fetchRow( "SELECT `status`, `schreibschutz` FROM `auftrag` WHERE `id` = :order_id ", ['order_id' => (int)$firstOrderId] ); if(empty($statusInfo) || $statusInfo['status'] !== 'abgeschlossen') { return false; } } $billaddress1 = $firstDatevRow['billaddress1']; $billaddress2 = $firstDatevRow['billaddress2']; $billaddress3 = $firstDatevRow['billaddress3']; $shipaddress1 = $firstDatevRow['shipaddress1']; $shipaddress2 = $firstDatevRow['shipaddress2']; $shipaddress3 = $firstDatevRow['shipaddress3']; $billStreet3 = ''; $shipStreet3 = ''; if(!empty($billaddress1)) { $billStreet = $billaddress1; if(is_numeric($billaddress2)) { $billStreet .= ' ' . $billaddress2; $billStreet2 = $billaddress3; } else { $billStreet2 = $billaddress2; $billStreet3 = $billaddress3; } } else { $billStreet = $billaddress2; if(is_numeric($billaddress3)) { $billStreet .= ' ' . $billaddress3; } else { $billStreet2 = $billaddress2; } } if(!empty($shipaddress1)) { $shipStreet = $shipaddress1; if(is_numeric($shipaddress2)) { $shipStreet .= ' ' . $shipaddress2; $shipStreet2 = $shipaddress3; } else { $shipStreet2 = $shipaddress2; $shipStreet3 = $shipaddress3; } } else { $shipStreet = $shipaddress2; if(is_numeric($shipaddress3)) { $shipStreet .= ' ' . $shipaddress3; } else { $shipStreet2 = $shipaddress2; } } if (empty($order) && $createOrder) { $address = [ 'name' => $firstDatevRow['buyername'], 'kundennummer' => $this->app->erp->GetNextKundennummer(), 'strasse' => $billStreet, 'addresszusatz' => $billStreet2, 'ort' => $firstDatevRow['billcity'], 'plz' => $firstDatevRow['billpostalcode'], 'land' => $firstDatevRow['billcountry'], ]; $address['id'] = (int)$this->app->erp->InsertUpdateAdresse($address); $orderId = (int)$this->app->erp->CreateAuftrag($address['id']); $this->app->erp->LoadAuftragStandardwerte($orderId, $address['id']); $order = [ 'name' => $firstDatevRow['buyername'], 'strasse' => $billStreet, 'addresszusatz' => $billStreet2, 'ort' => $firstDatevRow['billcity'], 'plz' => $firstDatevRow['billpostalcode'], 'land' => $firstDatevRow['billcountry'], 'email' => $firstDatevRow['buyeremail'], 'lieferstrasse' => $shipStreet, 'lieferaddresszusatz' => $shipStreet2, 'lieferland' => $firstDatevRow['shipcountry'], 'versandart' => $firstDatevRow['carrier'], 'lieferort' => $firstDatevRow['shipcity'], 'lieferplz' => $firstDatevRow['shippostalcode'], 'liefername' => $firstDatevRow['recipientname'], ]; if ($isOrderFbm && !empty($this->config['projectfbm'])) { $order['projekt'] = $this->config['projectfbm']; } elseif (!$isOrderFbm && !empty($this->config['projectfba'])) { $order['projekt'] = $this->config['projectfba']; } if (!empty($this->config['paymentmethod'])) { $order['versandart'] = $this->config['paymentmethod']; } $this->app->DB->UpdateArr('auftrag', $orderId, 'id', $order, true); $this->db->perform( 'UPDATE `auftrag` SET `internet` = :orderid WHERE `id` = :id', [ 'orderid' => $amazonOrderId, 'id' => $orderId, ] ); $this->app->erp->BelegFreigabe('auftrag', $orderId); $orderCreated = true; $order = $this->db->fetchCol( "SELECT `id` FROM `auftrag` WHERE `internet` = :amazonorderid AND `internet` <> '' AND `status` <> 'storniert' ", ['amazonorderid' => $amazonOrderId] ); $this->app->erp->AuftragProtokoll($orderId, 'Auftrag erstellt durch AmaInvoice'); } if (empty($order)) { return false; //throw new InvalidArgumentException(sprintf('order %s not found', $amazonOrderId)); } $order = (int)reset($order); if ($type === 'invoice') { if ($number !== null) { $invoice = $this->db->fetchCol( 'SELECT `id` FROM `rechnung` WHERE `auftragid` = :orderid AND `belegnr` = :number', ['orderid' => $order, 'number' => $number] ); } else { $invoice = $this->db->fetchCol( 'SELECT `id` FROM `rechnung` WHERE `auftragid` = :orderid', ['orderid' => $order] ); } if (!empty($invoice)) { return false; } $forceApproval = $this->app->erp->Firmendaten('schnellanlegen_ohnefreigabe') == '1'; if($forceApproval) { $db = $this->app->Conf->WFdbname; $this->app->erp->firmendaten[$db]['schnellanlegen_ohnefreigabe'] = 0; } $invoice = (int)$this->app->erp->WeiterfuehrenAuftragZuRechnung($order); if($isOrderFbm && !empty($statusInfo)) { $this->db->perform( 'UPDATE `auftrag` SET `status` = :status, `schreibschutz` = :readonly WHERE `id` = :order_id', [ 'status' => $statusInfo['status'], 'readonly' => (int)$statusInfo['schreibschutz'], 'order_id' => $order ] ); } if($forceApproval) { $this->app->erp->firmendaten[$db]['schnellanlegen_ohnefreigabe'] = '1'; } $invoiceArr = [ 'name' => $firstDatevRow['buyername'], 'strasse' => $billStreet, 'addresszusatz' => $billStreet2, 'abteilung' => $billStreet3, 'ort' => $firstDatevRow['billcity'], 'plz' => $firstDatevRow['billpostalcode'], 'land' => $firstDatevRow['billcountry'], 'email' => $firstDatevRow['buyeremail'], 'belegnr' => $number === null ? $firstDatevRow['inv_rech_nr'] : $number, ]; $orderArr = [ 'name' => $firstDatevRow['buyername'], 'strasse' => $billStreet, 'addresszusatz' => $billStreet2, 'abteilung' => $billStreet3, 'ort' => $firstDatevRow['billcity'], 'plz' => $firstDatevRow['billpostalcode'], 'land' => $firstDatevRow['billcountry'], 'email' => $firstDatevRow['buyeremail'], ]; if( $firstDatevRow['buyername'] !== $firstDatevRow['recipientname'] || $billStreet !== $shipStreet || $billStreet2 !== $shipStreet2 ) { $orderArr['abweichendelieferadresse'] = 1; $orderArr['liefername'] = $firstDatevRow['recipientname']; $orderArr['lieferstrasse'] = $shipStreet; $orderArr['lieferadresszusatz'] = $shipStreet2; $orderArr['lieferabteilung'] = $shipStreet3; $orderArr['lieferort'] = $firstDatevRow['shipcity']; $orderArr['lieferplz'] = $firstDatevRow['shippostalcode']; $orderArr['lieferland'] = $firstDatevRow['shipcountry']; } $this->app->DB->UpdateArr('auftrag', $order, 'id', $orderArr, true); $this->app->DB->UpdateArr('rechnung', $invoice, 'id', $invoiceArr, true); $this->app->erp->AuftragProtokoll($order, 'Rechnung erstellt durch AmaInvoice'); $this->app->erp->BelegFreigabe('rechnung', $invoice); $soll = 0; foreach ($positions as $position) { $soll += (float)str_replace(',', '.', $position['brutto_total']); } $this->addPositions('invoice', $invoice, $positions, $orderCreated ? $order : null); $this->addDiscountArticles( 'invoice', $invoice, $datevRows, $positions, $orderCreated ? $order : null ); $this->addShippingArticles( 'invoice', $invoice, $datevRows, $positions, $orderCreated ? $order : null ); $this->app->erp->RechnungNeuberechnen($invoice); $this->db->perform( "UPDATE `rechnung` SET `datum` = :date, `waehrung` = :currency, `soll` = :soll, `extsoll` = :soll, schreibschutz = 1, `status` = 'versendet' WHERE `id` = :invoiceId", [ 'date' => $firstDatevRow['date'], 'currency' => $firstDatevRow['currency'], 'invoiceId' => $invoice, 'soll' => $soll, ] ); if(!empty($firstDatevRow['fulfillmentchannel']) && $firstDatevRow['fulfillmentchannel'] === 'AFN') { $this->db->perform( 'UPDATE `rechnung` SET `ist` = `soll` WHERE `id` = :invoiceId', [ 'invoiceId' => $invoice, ] ); } $this->app->erp->PDFArchivieren('rechnung', $invoice, true); if (empty($pdfFile) && !empty($firstDatevRow['documentlink'])) { $content = file_get_contents($firstDatevRow['documentlink']); $pdfFile = basename($firstDatevRow['documentlink']) . '.pdf'; file_put_contents($this->app->erp->GetTMP() . $pdfFile, $content); $this->archiveDocument('invoice', $invoice, $pdfFile); if (is_file($this->app->erp->GetTMP() . $pdfFile)) { unlink($this->app->erp->GetTMP() . $pdfFile); } } elseif (!empty($pdfFile)) { $this->archiveDocument('invoice', $invoice, $pdfFile); } if ($orderCreated && !$isOrderFbm) { $this->db->perform( "UPDATE `auftrag` SET `datum` = :date, `waehrung` = :currency, `gesamtsumme` = :soll, `extsoll` = :soll, schreibschutz = 1, `status` = 'abgeschlossen' WHERE `id` = :orderId", [ 'date' => $firstDatevRow['date'], 'currency' => $firstDatevRow['currency'], 'orderId' => $order, 'soll' => $soll, ] ); $this->app->erp->PDFArchivieren('auftrag', $order, true); } return true; } if ($type === 'returnorder') { $invoices = $this->db->fetchCol( 'SELECT `id` FROM `rechnung` WHERE `auftragid` = :orderid', ['orderid' => $order] ); if (empty($invoices)) { throw new InvalidArgumentException('invoice not found'); } $returnOrder = $this->db->fetchCol( 'SELECT `id` FROM `gutschrift` WHERE `rechnungid` IN (:invoiceids) AND `belegnr` = :number', ['invoiceids' => $invoices, 'number' => $number] ); if (!empty($returnOrder)) { return false; } $invoice = reset($invoices); $forceApproval = $this->app->erp->Firmendaten('schnellanlegen_ohnefreigabe') == '1'; if($forceApproval) { $db = $this->app->Conf->WFdbname; $this->app->erp->firmendaten[$db]['schnellanlegen_ohnefreigabe'] = 0; } $returnOrder = $this->app->erp->WeiterfuehrenRechnungZuGutschrift($invoice); if($forceApproval) { $this->app->erp->firmendaten[$db]['schnellanlegen_ohnefreigabe'] = 1; } $this->app->erp->RechnungProtokoll($invoice, 'Gutschrift erstellt durch AmaInvoice'); $this->db->perform( 'UPDATE `gutschrift` SET `belegnr` = :number WHERE `id` = :id LIMIT 1', [ 'number' => (string)$number, 'id' => (int)$returnOrder, ] ); $this->app->erp->BelegFreigabe('gutschrift', $returnOrder); $soll = 0; foreach ($datevRows as $datevRow) { $soll += (float)str_replace(',', '.', $datevRow['brutto']); } $this->addPositions('returnorder', $returnOrder, $positions); $this->addDiscountArticles('returnorder', $returnOrder, $datevRows, $positions); $this->addShippingArticles('returnorder', $returnOrder, $datevRows, $positions); $this->app->erp->GutschriftNeuberechnen($returnOrder); $this->db->perform( "UPDATE `gutschrift` SET `datum` = :date, `waehrung` = :currency, `soll` = :soll, `extsoll` = :soll, schreibschutz = 1, `status` = 'versendet' WHERE `id` = :returnorderid", [ 'date' => $firstDatevRow['date'], 'currency' => $firstDatevRow['currency'], 'returnorderid' => $returnOrder, 'soll' => $soll, ] ); $this->app->erp->PDFArchivieren('gutschrift', $returnOrder, true); if (empty($pdfFile) && !empty($firstDatevRow['documentlink'])) { $content = file_get_contents($firstDatevRow['documentlink']); $pdfFile = basename($firstDatevRow['documentlink']) . '.pdf'; file_put_contents($this->app->erp->GetTMP() . $pdfFile, $content); $this->archiveDocument('returnorder', $returnOrder, $pdfFile); if (is_file($this->app->erp->GetTMP() . $pdfFile)) { unlink($this->app->erp->GetTMP() . $pdfFile); } } elseif (!empty($pdfFile)) { $this->archiveDocument('returnorder', $returnOrder, $pdfFile); } return true; } return false; } /** * @param string $file * @param string $type * @param string $status */ public function markFile($file, $type, $status = ''): void { $this->db->perform( 'INSERT INTO `amainvoice_files` (`filename`, `type`, `status`) VALUES (:filename, :type, :status)', ['filename' => $file, 'type' => $type, 'status' => (string)$status] ); } /** * @param string $file */ public function cleanFile($file): void { $file = $this->app->erp->GetTMP() . $file; if (!is_file($file)) { return; } @unlink($file); } /** * @param string $file * * @return array */ public function getPositionFromDatevCsv($file): array { $handle = fopen($file, 'rb'); if (empty($handle)) { throw new InvalidArgumentException('could not open file ' . $file); } $firstLine = fgetcsv($handle, 0, ';'); $from = $firstLine[14]; $year = substr($from, 0, 4); $return = []; while ($row = fgetcsv($handle, 0, ';')) { $brutto = $row[0]; $soll = $row[1] === 'S'; $currency = $row[2]; $exchangerate = $row[3]; $konto = $row[6]; $gegenkonto = $row[7]; $bu = $row[8]; $date = $row[9]; if (strlen($date) < 4) { $date = '0' . $date; } $day = substr($date, 0, 2); $month = substr($date, 2, 2); $date = $year . '-' . $month . '-' . $day; $number = $row[10]; $amzOrderId = $row[52]; $marketplace = $row[50]; $ustId = $row[48]; $tax = $row[40]; $euCountry = $row[39]; $customerUstId = $row[58]; $storageCountry = $row[54]; $country = $row[56]; $type = $soll ? 'invoice' : 'returnorder'; if (strpos($number, 'GS') === 0) { $type = 'returnorder'; } $return[$type][$amzOrderId][$number] = [ 'brutto' => $brutto, 'currency' => $currency, 'exchangerate' => $exchangerate, 'konto' => $konto, 'gegenkonto' => $gegenkonto, 'bu' => $bu, 'date' => $date, 'marketplace' => $marketplace, 'eucountry' => $euCountry, 'tax' => $tax, 'ustid' => $ustId, 'customerustid' => $customerUstId, 'storageCountry' => $storageCountry, 'country' => $country, ]; } fclose($handle); return $return; } /** * @param string $file */ public function executeImportDateFile($file) { $ext = substr($file, -3); $type = $ext === 'rem' ? 'returnorder' : 'invoice'; $apiPositions = $this->executeGetPostionsByApiAndFilename($file); foreach ($apiPositions[$type] as $amazonOrderId => $apiSubPositions) { $document = [$apiSubPositions['document']]; foreach ($apiSubPositions['positions'] as $numberInvoice => $apiSubPosition) { try { $this->createDocument( $type, $amazonOrderId, null, $document, $apiSubPosition, '', $this->isCreateOrderActive() ); } catch (InvalidArgumentException $e) { } } } $this->markFile($file, 'api_' . $type, 'imported'); } /** * @param bool $isReturnOrder * @param bool $createOrder */ public function executeImportDateDbEntries($isReturnOrder, $createOrder = false): void { $apiPositions = $this->getNotImportedPositionsFromDb($isReturnOrder, $createOrder); $type = $isReturnOrder ? 'returnorder' : 'invoice'; foreach ($apiPositions[$type] as $amazonOrderId => $apiSubPositions) { $document = [$apiSubPositions['document']]; foreach ($apiSubPositions['positions'] as $numberInvoice => $apiSubPosition) { try { $this->createDocument( $type, $amazonOrderId, null, $document, $apiSubPosition, '', $createOrder ); } catch (InvalidArgumentException $e) { } } } } /** * @param DateTimeInterface $dateFrom * @param DateTimeInterface $dateTo * @param bool $isReturnOrder */ public function markDbEntriesToImport($dateFrom, $dateTo, $isReturnOrder): void { if (!$isReturnOrder) { $this->loadConfig(); $createOrder = $this->isCreateOrderActive(); if (!$createOrder) { $this->db->perform( "UPDATE `amazoninvoice_position` SET `create` = 1 WHERE `doctype_id` = 0 AND `create_order` = 0 AND `rem_gs_nr` = '' AND `inv_date` >= :dateFrom AND `inv_date` >= :dateTo", [ 'dateFrom' => $dateFrom->format('Y-m-d'), 'dateTo' => $dateTo->format('Y-m-d'), ] ); } else { $this->db->perform( "UPDATE `amazoninvoice_position` SET `create` = 1, `create_order` = 1 WHERE `doctype_id` = 0 AND `create_order` = 0 AND `rem_gs_nr` = '' AND `inv_date` >= :dateFrom AND `inv_date` >= :dateTo", [ 'dateFrom' => $dateFrom->format('Y-m-d'), 'dateTo' => $dateTo->format('Y-m-d'), ] ); } } else { $this->db->perform( "UPDATE `amazoninvoice_position` SET `create` = 1 WHERE `doctype_id` = 0 AND `create_order` = 0 AND `rem_gs_nr` <> '' AND `rem_gs_nr` >= :dateFrom AND `rem_gs_nr` >= :dateTo", [ 'dateFrom' => $dateFrom->format('Y-m-d'), 'dateTo' => $dateTo->format('Y-m-d'), ] ); } } /** * @param string $file * * @return array|array[] */ public function executeGetPostionsByApiAndFilename($file): array { $date = substr($file, 0, 10); $ext = substr($file, -3); $arr = $this->getDocumentsByApi($date, $date, $ext); $arr = $this->insertFromApiReturn($arr, $date, $ext === 'rem'); return $this->getPositionsFromApiRequest( $arr, $ext === 'rem' ); } /** * @param bool $isReturnOrder * @param bool $createOrder * * @return array|array[] */ public function getNotImportedPositionsFromDb($isReturnOrder, $createOrder = false, $hours = 8): array { if (!$isReturnOrder) { if (!$createOrder) { $arr = $this->db->fetchAll( "SELECT ap.* FROM `amazoninvoice_position` AS `ap` WHERE ap.`create` = 1 AND ap.doctype_id = 0 AND `create_order` = 0 AND ap.rem_gs_nr = '' ORDER BY ap.`inv_date`, ap.id" ); } else { $arr = $this->db->fetchAll( "SELECT ap.* FROM `amazoninvoice_position` AS `ap` WHERE ap.`create` = 1 AND ap.doctype_id = 0 AND `create_order` = 1 AND ap.rem_gs_nr = '' AND ap.created_at < DATE_SUB(NOW(), INTERVAL :hours HOUR) ORDER BY ap.`inv_date`, ap.id", [ 'hours' => $hours, ] ); } } else { $arr = $this->db->fetchAll( "SELECT ap.* FROM `amazoninvoice_position` AS `ap` WHERE ap.`create` = 1 AND ap.doctype_id = 0 AND ap.rem_gs_nr <> '' ORDER BY ap.`rem_date`, ap.id" ); } return $this->getPositionsFromApiRequest( $arr, $isReturnOrder ); } /** * @param array $arr * @param string $date * @param bool $isReturnOrder * * @return array */ public function insertFromApiReturn($arr, $date, $isReturnOrder): array { if ($isReturnOrder) { $inDb = $this->db->fetchPairs( 'SELECT `rem_gs_nr`, `id` FROM `amazoninvoice_position` WHERE `rem_date` = :date', ['date' => $date] ); } else { $inDb = $this->db->fetchPairs( 'SELECT `inv_rech_nr`, `id` FROM `amazoninvoice_position` WHERE `inv_date` = :date', ['date' => $date] ); } foreach ($arr as $key => $row) { if ($isReturnOrder) { if (!empty($inDb[$row['rem_gs_nr']])) { $arr[$key]['id'] = $inDb[$row['rem_gs_nr']]; continue; } } elseif (!empty($inDb[$row['inv_rech_nr']])) { $arr[$key]['id'] = $inDb[$row['inv_rech_nr']]; continue; } $this->db->perform( 'INSERT INTO `amazoninvoice_position` ( `inv_rech_nr`,`inv_date`,`amazonorderid`,`shipmentdate`,`buyeremail`,`buyerphonenumber`, `buyername`,`sku`,`productname`,`quantitypurchased`,`quantityshipped`,`currency`,`mwst`, `taxrate`,`brutto_total`,`netto_total`,`tax_total`,`itemprice`,`itemprice_netto`,`itemprice_tax`, `shippingprice`,`shippingprice_netto`,`shippingprice_tax`,`giftwrapprice`,`giftwrapprice_netto`, `giftwrapprice_tax`,`itempromotiondiscount`,`itempromotiondiscount_netto`, `itempromotiondiscount_tax`,`shippromotiondiscount`,`shippromotiondiscount_netto`, `shippromotiondiscount_tax`,`giftwrappromotiondiscount`,`giftwrappromotiondiscount_netto`, `giftwrappromotiondiscount_tax`,`shipservicelevel`,`recipientname`,`shipaddress1`,`shipaddress2`, `shipaddress3`,`shipcity`,`shipstate`,`shippostalcode`,`shipcountry`,`shipphonenumber`, `billaddress1`,`billaddress2`,`billaddress3`,`billcity`,`billstate`,`billpostalcode`, `billcountry`,`carrier`,`trackingnumber`,`fulfillmentcenterid`,`fulfillmentchannel`, `saleschannel`,`asin`,`conditiontype`,`quantityavailable`,`isbusinessorder`,`uid`,`vatcheck`, `documentlink`,`order_id`,`rem_gs_nr`,`orderid`, `rem_date`,`returndate`,`buyercompanyname`,`quantity`,`remreturnshipcost`, `remsondererstattung`,`itempromotionid`,`reason`,`rem_gs_nr_real`, `created_at` ) VALUES ( :inv_rech_nr, :inv_date, :amazonorderid, :shipmentdate, :buyeremail, :buyerphonenumber, :buyername, :sku, :productname, :quantitypurchased, :quantityshipped, :currency, :mwst, :taxrate, :brutto_total, :netto_total, :tax_total, :itemprice, :itemprice_netto, :itemprice_tax, :shippingprice, :shippingprice_netto, :shippingprice_tax, :giftwrapprice, :giftwrapprice_netto, :giftwrapprice_tax, :itempromotiondiscount, :itempromotiondiscount_netto, :itempromotiondiscount_tax, :shippromotiondiscount, :shippromotiondiscount_netto, :shippromotiondiscount_tax, :giftwrappromotiondiscount, :giftwrappromotiondiscount_netto, :giftwrappromotiondiscount_tax, :shipservicelevel, :recipientname, :shipaddress1, :shipaddress2, :shipaddress3, :shipcity, :shipstate, :shippostalcode, :shipcountry, :shipphonenumber, :billaddress1, :billaddress2, :billaddress3, :billcity, :billstate, :billpostalcode, :billcountry, :carrier, :trackingnumber, :fulfillmentcenterid, :fulfillmentchannel, :saleschannel, :asin, :conditiontype, :quantityavailable, :isbusinessorder, :uid, :vatcheck, :documentlink, :order_id, :rem_gs_nr, :orderid, :rem_date, :returndate, :buyercompanyname, :quantity, :remreturnshipcost, :remsondererstattung, :itempromotionid, :reason, :rem_gs_nr_real, NOW() ) ', [ 'inv_rech_nr' => (string)$row['inv_rech_nr'], 'inv_date' => (string)$row['inv_date'], 'amazonorderid' => (string)$row['amazonorderid'], 'shipmentdate' => (string)$row['shipmentdate'], 'buyeremail' => (string)$row['buyeremail'], 'buyerphonenumber' => (string)$row['buyerphonenumber'], 'buyername' => (string)$row['buyername'], 'sku' => (string)$row['sku'], 'productname' => (string)$row['productname'], 'quantitypurchased' => (string)$row['quantitypurchased'], 'quantityshipped' => (string)$row['quantityshipped'], 'currency' => (string)$row['currency'], 'mwst' => (string)$row['mwst'], 'taxrate' => (string)$row['taxrate'], 'brutto_total' => (string)$row['brutto_total'], 'netto_total' => (string)$row['netto_total'], 'tax_total' => (string)$row['tax_total'], 'itemprice' => (string)$row['itemprice'], 'itemprice_netto' => (string)$row['itemprice_netto'], 'itemprice_tax' => (string)$row['itemprice_tax'], 'shippingprice' => (string)$row['shippingprice'], 'shippingprice_netto' => (string)$row['shippingprice_netto'], 'shippingprice_tax' => (string)$row['shippingprice_tax'], 'giftwrapprice' => (string)$row['giftwrapprice'], 'giftwrapprice_netto' => (string)$row['giftwrapprice_netto'], 'giftwrapprice_tax' => (string)$row['giftwrapprice_tax'], 'itempromotiondiscount' => (string)$row['itempromotiondiscount'], 'itempromotiondiscount_netto' => (string)$row['itempromotiondiscount_netto'], 'itempromotiondiscount_tax' => (string)$row['itempromotiondiscount_tax'], 'shippromotiondiscount' => (string)$row['shippromotiondiscount'], 'shippromotiondiscount_netto' => (string)$row['shippromotiondiscount_netto'], 'shippromotiondiscount_tax' => (string)$row['shippromotiondiscount_tax'], 'giftwrappromotiondiscount' => (string)$row['giftwrappromotiondiscount'], 'giftwrappromotiondiscount_netto' => (string)$row['giftwrappromotiondiscount_netto'], 'giftwrappromotiondiscount_tax' => (string)$row['giftwrappromotiondiscount_tax'], 'shipservicelevel' => (string)$row['shipservicelevel'], 'recipientname' => (string)$row['recipientname'], 'shipaddress1' => (string)$row['shipaddress1'], 'shipaddress2' => (string)$row['shipaddress2'], 'shipaddress3' => (string)$row['shipaddress3'], 'shipcity' => (string)$row['shipcity'], 'shipstate' => (string)$row['shipstate'], 'shippostalcode' => (string)$row['shippostalcode'], 'shipcountry' => (string)$row['shipcountry'], 'shipphonenumber' => (string)$row['shipphonenumber'], 'billaddress1' => (string)$row['billaddress1'], 'billaddress2' => (string)$row['billaddress2'], 'billaddress3' => (string)$row['billaddress3'], 'billcity' => (string)$row['billcity'], 'billstate' => (string)$row['billstate'], 'billpostalcode' => (string)$row['billpostalcode'], 'billcountry' => (string)$row['billcountry'], 'carrier' => (string)$row['carrier'], 'trackingnumber' => (string)$row['trackingnumber'], 'fulfillmentcenterid' => (string)$row['fulfillmentcenterid'], 'fulfillmentchannel' => (string)$row['fulfillmentchannel'], 'saleschannel' => (string)$row['saleschannel'], 'asin' => (string)$row['asin'], 'conditiontype' => (string)$row['conditiontype'], 'quantityavailable' => (string)$row['quantityavailable'], 'isbusinessorder' => (string)$row['isbusinessorder'], 'uid' => (string)$row['uid'], 'vatcheck' => (string)$row['vatcheck'], 'documentlink' => (string)$row['documentlink'], 'order_id' => (string)$row['order_id'], 'rem_gs_nr' => (string)$row['rem_gs_nr'], 'orderid' => (string)$row['orderid'], 'rem_date' => (string)$row['rem_date'], 'returndate' => (string)$row['returndate'], 'buyercompanyname' => (string)$row['buyercompanyname'], 'quantity' => (string)$row['quantity'], 'remreturnshipcost' => (string)$row['remreturnshipcost'], 'remsondererstattung' => (string)$row['remsondererstattung'], 'itempromotionid' => (string)$row['itempromotionid'], 'reason' => (string)$row['reason'], 'rem_gs_nr_real' => (string)$row['rem_gs_nr_real'], ] ); $arr[$key]['id'] = $this->db->lastInsertId(); } return $arr; } /** * @param array $arr * @param bool $isReturnOrder * * @return array|array[] */ public function getPositionsFromApiRequest($arr, $isReturnOrder = false): array { if (empty($arr)) { return []; } $type = $isReturnOrder ? 'returnorder' : 'invoice'; $return = [$type => []]; $numberKey = $isReturnOrder ? 'rem_gs_nr' : 'inv_rech_nr'; $dateKey = $isReturnOrder ? 'rem_date' : 'inv_date'; foreach ($arr as $row) { $amazonOrder = empty($row['amazonorderid']) ? $row['orderid'] : $row['amazonorderid']; $number = $row[$numberKey]; $qty = empty($row['quantity']) ? $row['quantitypurchased'] : $row['quantity']; $taxRate = empty($row['mwst']) ? 0 : $row['taxrate']; $row['taxrate'] = (float)$row['taxrate']; $row['date'] = $row[$dateKey]; $row['quantity'] = $qty; $row['bruttototal'] = $row['brutto_total']; $row['nettototal'] = $row['netto_total']; $row['preis'] = empty($row['mwst']) ? $row['itemprice_netto'] / $qty : $row['itemprice'] / (1 + $row['taxrate'] / 100.0) / $qty; $row['itemdiscount_price'][$taxRate] = empty($row['mwst']) ? $row['itempromotiondiscount_netto'] : $row['itempromotiondiscount'] / (1 + $row['taxrate'] / 100.0); $row['shipping_net_price'][$taxRate] = empty($row['mwst']) ? $row['shippingprice_netto'] : $row['shippingprice'] / (1 + $row['taxrate'] / 100.0); $row['shippingdiscount_price'][$taxRate] = empty($row['mwst']) ? $row['shippromotiondiscount_netto'] : $row['shippromotiondiscount'] / (1 + $row['taxrate'] / 100.0); $row['giftwrap_price'] = empty($row['mwst']) ? $row['giftwrapprice_netto'] / $qty : $row['giftwrapprice'] / (1 + $row['taxrate'] / 100.0) / $qty; $row['giftwrapdiscount_price'] = empty($row['mwst']) ? $row['giftwrappromotiondiscount_netto'] : $row['giftwrappromotiondiscount'] / (1 + $row['taxrate'] / 100.0); $row['giftwrap_price_with_discount'] = round( (float)$row['giftwrapprice'] + (float)$row['giftwrappromotiondiscount'], 2 ) / $qty; $row['giftwrap_price_net_with_discount'] = empty($row['mwst']) ? round( (float)$row['giftwrapprice_netto'] + (float)$row['giftwrappromotiondiscount_netto'], 2 ) / $qty : round( (float)$row['giftwrapprice'] + (float)$row['giftwrappromotiondiscount'], 2 ) / $qty / (1.0 + $row['taxrate'] / 100.0); $row['shipping_price_with_discount'][$taxRate] = round( (float)$row['shippingprice'] + (float)$row['shippromotiondiscount'], 2 ); if (!isset($return[$type][$amazonOrder]['document'])) { $return[$type][$amazonOrder]['document'] = $row; } else { $return[$type][$amazonOrder]['document']['brutto_total'] = round( (float)$return[$type][$amazonOrder]['document']['brutto_total'] + (float)$row['brutto_total'] , 2 ); $return[$type][$amazonOrder]['document']['nettototal'] = round( (float)$return[$type][$amazonOrder]['document']['nettototal'] + (float)$row['nettototal'] , 2 ); if (!isset($return[$type][$amazonOrder]['document']['shipping_net_price'][$taxRate])) { $return[$type][$amazonOrder]['document']['shipping_net_price'][$taxRate] = 0.0; } $return[$type][$amazonOrder]['document']['shipping_net_price'][$taxRate] = (float)$return[$type][$amazonOrder]['document']['shipping_net_price'][$taxRate] + (float)$row['shipping_net_price'][$taxRate]; if (!isset($return[$type][$amazonOrder]['document']['shippingdiscount_price'][$taxRate])) { $return[$type][$amazonOrder]['document']['shippingdiscount_price'][$taxRate] = 0.0; } $return[$type][$amazonOrder]['document']['shippingdiscount_price'][$taxRate] = (float)$return[$type][$amazonOrder]['document']['shippingdiscount_price'][$taxRate] + (float)$row['shippingdiscount_price'][$taxRate]; if (!isset($return[$type][$amazonOrder]['document']['shipping_price_with_discount'][$taxRate])) { $return[$type][$amazonOrder]['document']['shipping_price_with_discount'][$taxRate] = 0.0; } $return[$type][$amazonOrder]['document']['shipping_price_with_discount'][$taxRate] = (float)$return[$type][$amazonOrder]['document']['shipping_price_with_discount'][$taxRate] + (float)$row['shipping_price_with_discount'][$taxRate]; if (!isset($return[$type][$amazonOrder]['document']['itemdiscount_price'][$taxRate])) { $return[$type][$amazonOrder]['document']['itemdiscount_price'][$taxRate] = 0.0; } $return[$type][$amazonOrder]['document']['itemdiscount_price'][$taxRate] = (float)$return[$type][$amazonOrder]['document']['itemdiscount_price'][$taxRate] + (float)$row['itemdiscount_price'][$taxRate]; } $return[$type][$amazonOrder]['document']['brutto'] = $return[$type][$amazonOrder]['document']['brutto_total']; $return[$type][$amazonOrder]['positions'][$number][] = $row; } return $return; } /** * @param string $file * * @return array */ public function getPositionFromExportCsv($file): array { $handle = fopen($file, 'r'); if (empty($handle)) { throw new InvalidArgumentException('could not open file ' . $file); } $header = fgetcsv($handle, 0, ';'); $headerToKey = array_flip($header); $isReturnOrder = in_array('rem_gs_nr', $header); $amazonOrderKey = isset($headerToKey['orderid']) ? $headerToKey['orderid'] : null; if ($amazonOrderKey === null) { $amazonOrderKey = isset($headerToKey['amazonorderid']) ? $headerToKey['amazonorderid'] : null; } $returnOrderKey = isset($headerToKey['rem_gs_nr']) ? $headerToKey['rem_gs_nr'] : null; $returnOderDateKey = isset($headerToKey['rem_date']) ? $headerToKey['rem_date'] : null; $invDateKey = isset($headerToKey['inv_date']) ? $headerToKey['inv_date'] : null; $invoiceKey = isset($headerToKey['inv_rech_nr']) ? $headerToKey['inv_rech_nr'] : null; $skuKey = isset($headerToKey['sku']) ? $headerToKey['sku'] : null; $quantityKey = isset($headerToKey['quantity']) ? $headerToKey['quantity'] : null; if ($quantityKey === null) { $quantityKey = isset($headerToKey['quantitypurchased']) ? $headerToKey['quantitypurchased'] : null; } $currencyKey = isset($headerToKey['currency']) ? $headerToKey['currency'] : null; $taxrateKey = isset($headerToKey['taxrate']) ? $headerToKey['taxrate'] : null; $bruttoTotalKey = isset($headerToKey['brutto_total']) ? $headerToKey['brutto_total'] : null; $nettoTotalKey = isset($headerToKey['netto_total']) ? $headerToKey['netto_total'] : null; $mwstKey = isset($headerToKey['mwst']) ? $headerToKey['mwst'] : null; $dateKey = $isReturnOrder ? $returnOderDateKey : $invDateKey; $numberKey = $isReturnOrder ? $returnOrderKey : $invoiceKey; $type = $isReturnOrder ? 'returnorder' : 'invoice'; $return = [ $type => [], ]; while ($row = fgetcsv($handle, 0, ';')) { $amazonOrder = $row[$amazonOrderKey]; $date = $row[$dateKey]; $number = $row[$numberKey]; $sku = $row[$skuKey]; $quantity = $row[$quantityKey]; $mwst = $row[$mwstKey]; $bruttoTotal = $row[$bruttoTotalKey]; $nettoTotal = $row[$nettoTotalKey]; $taxRate = $row[$taxrateKey]; $currency = $row[$currencyKey]; if (empty($amazonOrder) || empty($number)) { continue; } $return[$type][$amazonOrder][$number][] = [ 'date' => $date, 'sku' => $sku, 'quantity' => $quantity, 'mwst' => $mwst, 'bruttototal' => $bruttoTotal, 'nettototal' => $nettoTotal, 'taxrate' => $taxRate, 'currency' => $currency, ]; } fclose($handle); return $return; } /** * @return bool */ public function isCreateOrderActive() { return !empty($this->config['createorder']); } private function validateConfig(): void { if (!empty($this->config['firmkeyid']) || !empty($this->config['clientidentifier'])) { foreach (['firmkeyid', 'clientidentifier',] as $name) { if (!isset($this->config[$name])) { throw new InvalidArgumentException(sprintf('Config field %s not found', $name)); } } return; } if (!empty($this->config['ftp'])) { foreach (['host', 'user', 'pass', 'dir', 'port'] as $name) { if (!isset($this->config[$name])) { throw new InvalidArgumentException(sprintf('Config field %s not found', $name)); } } } else { if (empty($this->config['dir'])) { throw new InvalidArgumentException('Config dir not found'); } } } private function loadConfig(): void { $this->config = $this->getConfig(); if (!$this->useFtp) { $this->config['ftp'] = 0; } $this->validateConfig(); if (!empty($this->config['firmkeyid'])) { return; } if (!empty($this->config['ftp'])) { $host = $this->config['host']; $user = $this->config['user']; $pass = $this->config['pass']; $dir = $this->config['dir']; $port = $this->config['port']; $ftpConfig = new FtpConfig($host, $user, $pass, $dir, $port); $this->filesystem = $this->filesystemFactory->createFtp($ftpConfig); } else { $fileSystemConfig = [ 'permissions' => [ 'file' => [ 'public' => 0664, 'private' => 0664, ], 'dir' => [ 'public' => 0775, 'private' => 0775, ], ], ]; $this->filesystem = $this->filesystemFactory->createLocal($this->config['dir'], $fileSystemConfig); } } /** * @param string $doctype * @param int $doctypeId * * @return int|null */ private function getAmazonShopIdFromDocument($doctype, $doctypeId): ?int { if ($doctype === 'rechnung') { $invoiceId = $doctypeId; } else { $invoiceId = $this->db->fetchValue( 'SELECT `rechnungid` FROM `gutschrift` WHERE `id` = :id', ['id' => $doctypeId] ); } if (empty($invoiceId)) { return null; } $orderId = $this->db->fetchValue( 'SELECT `auftragid` FROM `rechnung` WHERE `id` = :id', ['id' => $invoiceId] ); if (empty($orderId)) { return null; } $shopId = (int)$this->db->fetchValue( 'SELECT `shop` FROM `auftrag` WHERE `id` = :id', ['id' => $orderId] ); if (empty($shopId)) { return null; } $shopId = $this->db->fetchValue( 'SELECT `id` FROM `shopexport` WHERE `id` = :id AND `modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($shopId)) { return null; } return (int)$shopId; } /** * @param int $shopId * * @return int|null */ private function getDiscountArticleIdByShopId($shopId): ?int { $discountArticle = $this->db->fetchRow( 'SELECT `art`.`id` FROM `shopexport` AS `s` LEFT JOIN `artikel` AS `art` ON `s`.`artikelrabatt` = art.id AND (`art`.`geloescht` = 0 OR `art`.`geloescht` IS NULL) AND (`art`.`intern_gesperrt` = 0 OR `art`.`intern_gesperrt` IS NULL) WHERE `s`.`id` = :id AND `s`.`modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($discountArticle)) { return null; } if ($discountArticle['id'] > 0) { return (int)$discountArticle['id']; } return $this->createDiscountArticleByShopId($shopId); } /** * @param int $shopId * * @return int|null */ private function getShippingArticleIdByShopId($shopId): ?int { $shippingArticle = $this->db->fetchRow( 'SELECT `art`.`id` FROM `shopexport` AS `s` LEFT JOIN `artikel` AS `art` ON `s`.`artikelporto` = art.id AND (`art`.`geloescht` = 0 OR `art`.`geloescht` IS NULL) AND (`art`.`intern_gesperrt` = 0 OR `art`.`intern_gesperrt` IS NULL) WHERE `s`.`id` = :id AND `s`.`modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($shippingArticle)) { return null; } if ($shippingArticle['id'] > 0) { return (int)$shippingArticle['id']; } return $this->createShippingArticleByShopId($shopId); } /** * @param int $shopId * * @return int|null */ private function getGiftWrapArticleIdByShopId($shopId): ?int { $json = $this->db->fetchValue( 'SELECT `einstellungen_json` FROM `shopexport` WHERE `id` = :id AND `modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($json)) { return null; } $json = @json_decode($json, true); if (empty($json) || empty($json['felder']) || empty($json['felder']['giftwrap'])) { return null; } return (int)$json['felder']['giftwrap']; } /** * @param int $shopId * * @return int|null */ private function createDiscountArticleByShopId($shopId): ?int { $shop = $this->db->fetchRow( 'SELECT `s`.`projekt`, `art`.`id` FROM `shopexport` AS `s` LEFT JOIN `artikel` AS `art` ON `s`.`artikelrabatt` = art.id AND (`art`.`geloescht` = 0 OR `art`.`geloescht` IS NULL) AND (`art`.`intern_gesperrt` = 0 OR `art`.`intern_gesperrt` IS NULL) WHERE `s`.`id` = :id AND `s`.`modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($shop) || !empty($shop['id'])) { return null; } $discountArticle = [ 'projekt' => $shop['projekt'], 'name_de' => 'Rabatt', 'name_en' => 'Discount', 'lagerartikel' => 0, ]; $discountArticle['nummer'] = $this->app->erp->GetNextArtikelnummer('', '1', $shop['projekt']); $discountArticleId = (int)$this->app->erp->InsertUpdateArtikel($discountArticle); if ($discountArticleId <= 0) { return null; } $this->db->perform( 'UPDATE `shopexport` SET `artikelrabatt` = :articleId WHERE `id` = :shopId ', [ 'articleId' => $discountArticleId, 'shopId' => $shopId, ] ); return $discountArticleId; } /** * @param int $shopId * * @return int|null */ private function createShippingArticleByShopId($shopId): ?int { $shop = $this->db->fetchRow( 'SELECT `s`.`projekt`, `art`.`id` FROM `shopexport` AS `s` LEFT JOIN `artikel` AS `art` ON `s`.`artikelporto` = art.id AND (`art`.`geloescht` = 0 OR `art`.`geloescht` IS NULL) AND (`art`.`intern_gesperrt` = 0 OR `art`.`intern_gesperrt` IS NULL) WHERE `s`.`id` = :id AND `s`.`modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($shop) || !empty($shop['id'])) { return null; } $firstShippingArticle = $this->db->fetchRow( 'SELECT `art`.`id` FROM `artikel` AS `art` LEFT JOIN `projekt` AS `p` ON `art`.projekt = p.id WHERE `art`.`porto` = 1 AND (`art`.`geloescht` IS NOT NULL OR `art`.geloescht = 0) AND (`art`.`intern_gesperrt` = 0 OR `art`.`intern_gesperrt` IS NULL) AND (`p`.`id` IS NULL OR p.`oeffentlich` = 1) ' ); if (!empty($firstShippingArticle)) { $this->db->perform( 'UPDATE `shopexport` SET `artikelporto` = :articleId WHERE `id` = :shopId ', [ 'articleId' => (int)$firstShippingArticle['id'], 'shopId' => $shopId, ] ); return (int)$firstShippingArticle['id']; } $shippingArticle = ['projekt' => $shop['projekt'], 'name_de' => 'Porto', 'proto' => 1]; $shippingArticle['nummer'] = $this->app->erp->GetNextArtikelnummer('', '1', $shop['projekt']); $shippingArticleId = (int)$this->app->erp->InsertUpdateArtikel($shippingArticle); if ($shippingArticleId <= 0) { return null; } $this->db->perform( 'UPDATE `shopexport` SET `artikelporto` = :articleId WHERE `id` = :shopId ', [ 'articleId' => $shippingArticleId, 'shopId' => $shopId, ] ); return $shippingArticleId; } /** * @param int $shopId * * @return int|null */ private function createGiftWrapArticle($shopId): ?int { $shop = $this->db->fetchRow( 'SELECT `projekt`, `einstellungen_json` FROM `shopexport` WHERE `id` = :id AND `modulename` = :module_name', [ 'id' => $shopId, 'module_name' => 'shopimporter_amazon', ] ); if (empty($shop)) { return null; } $giftwrapArticle = ['projekt' => $shop['projekt'], 'name_de' => 'Geschenkverpackung', 'lagerartikel' => 1]; $giftwrapArticle['nummer'] = $this->app->erp->GetNextArtikelnummer('', '1', $shop['projekt']); $giftwrapArticleId = (int)$this->app->erp->InsertUpdateArtikel($giftwrapArticle); if ($giftwrapArticleId <= 0) { return null; } $json = @json_decode($shop['einstellungen_json'], true); if (!is_array($json)) { $json = ['felder' => []]; } if (!isset($json['felder'])) { $json['felder'] = []; } $json['felder']['giftwrap'] = $giftwrapArticleId; $this->db->perform( 'UPDATE `shopexport` SET `einstellungen_json` = :json WHERE `id` = :id', [ 'json' => json_encode($json), 'id' => $shopId, ] ); return $giftwrapArticleId; } /** * @param string $document * @param int $positionId * @param float $tax */ private function changePositionTax($document, $positionId, $tax): void { if (!is_numeric($tax) || empty($positionId) || !in_array($document, ['rechnung', 'gutschrift'])) { return; } if ($document === 'rechnung') { $this->db->perform( 'UPDATE `rechnung_position` SET `steuersatz` = :tax WHERE `id` = :positionId', [ 'tax' => (float)$tax, 'positionId' => (int)$positionId, ] ); return; } if ($document === 'gutschrift') { $this->db->perform( 'UPDATE `gutschrift_position` SET `steuersatz` = :tax WHERE `id` = :positionId', [ 'tax' => (float)$tax, 'positionId' => (int)$positionId, ] ); return; } } /** * @param string $doctype * @param int $doctypeId * @param float|string $tax * @param float $price * * @return int|null */ private function addDiscountPosition($doctype, $doctypeId, $tax, $price): ?int { $shopId = $this->getAmazonShopIdFromDocument($doctype, $doctypeId); if ($shopId === null) { return null; } $discountArticleId = $this->getDiscountArticleIdByShopId($shopId); if ($discountArticleId === null) { $discountArticleId = $this->createDiscountArticleByShopId($shopId); if ($discountArticleId === null) { return null; } } $document = $this->getDocumentInfoForAddPosition($doctype, $doctypeId); if ($document === null) { return null; } $discountArticle = $this->getArticleInfoForAddPosition($document, $discountArticleId); if ($discountArticle === null) { return null; } $positionId = $this->app->erp->AddPositionManuellPreisNummer( $doctype, $doctypeId, 0, $discountArticle['nummer'], 1, $discountArticle['name'], $price, !empty($tax) && in_array($tax, ['normal', 'ermaessigt', 'befreit']) ? $tax : '', 0, 0, $document['waehrung'] ); if (is_numeric($tax)) { $this->changePositionTax($doctype, $positionId, $tax); } if (empty($positionId)) { return null; } return (int)$positionId; } /** * @param string $doctype * @param int $doctypeId * @param float|string $tax * @param float $price * * @return int|null */ private function addShippingPosition($doctype, $doctypeId, $tax, $price): ?int { $shopId = $this->getAmazonShopIdFromDocument($doctype, $doctypeId); if ($shopId === null) { return null; } $shippingArticleId = $this->getShippingArticleIdByShopId($shopId); if ($shippingArticleId === null) { $shippingArticleId = $this->createShippingArticleByShopId($shopId); if ($shippingArticleId === null) { return null; } } $document = $this->getDocumentInfoForAddPosition($doctype, $doctypeId); if ($document === null) { return null; } $shippingArticle = $this->getArticleInfoForAddPosition($document, $shippingArticleId); if ($shippingArticle === null) { return null; } $positionId = $this->app->erp->AddPositionManuellPreisNummer( $doctype, $doctypeId, 0, $shippingArticle['nummer'], 1, $shippingArticle['name'], $price, !empty($tax) && in_array($tax, ['normal', 'ermaessigt', 'befreit']) ? $tax : '', 0, 0, $document['waehrung'] ); if (is_numeric($tax)) { $this->changePositionTax($doctype, $positionId, $tax); } if (empty($positionId)) { return null; } return (int)$positionId; } /** * @param string $doctype * @param int $doctypeId * * @return array|null */ private function getDocumentInfoForAddPosition($doctype, $doctypeId): ?array { if ($doctype === 'rechnung') { $document = $this->db->fetchRow( 'SELECT `waehrung`, `sprache` FROM `rechnung` WHERE `id` = :id', ['id' => $doctypeId] ); } elseif ($doctype === 'gutschrift') { $document = $this->db->fetchRow( 'SELECT `waehrung`, `sprache` FROM `gutschrift` WHERE `id` = :id', ['id' => $doctypeId] ); } else { return null; } if (empty($document)) { return null; } if (empty($document['waehrung'])) { $document['waehrung'] = 'EUR'; } return $document; } /** * @param array $document * @param int $articleId * * @return array|null */ private function getArticleInfoForAddPosition($document, $articleId): ?array { $article = $this->db->fetchRow( 'SELECT `nummer`, `name_de`, `name_en` FROM `artikel` WHERE `id` = :id ', ['id' => $articleId] ); if (empty($article)) { return null; } $name = $article['name_de']; if ( !empty($article['name_en']) && !empty($document['sprache']) && !in_array($document['sprache'], ['deutsch', 'german', 'de', 'DE']) ) { $name = $article['name_en']; } $article['name'] = $name; return $article; } /** * @param string $doctype * @param int $doctypeId * @param int $quantity * @param float $price * @param null|int $orderId * * @return int|null */ private function addGiftWrapPositon($doctype, $doctypeId, $quantity, $price, $orderId = null): ?int { if ($doctype !== 'rechnung') { $createOrderPosition = false; } $shopId = $this->getAmazonShopIdFromDocument($doctype, $doctypeId); if ($shopId === null) { return null; } $giftWrapArticleId = $this->getGiftWrapArticleIdByShopId($shopId); if ($giftWrapArticleId === null) { $giftWrapArticleId = $this->createGiftWrapArticle($shopId); if ($giftWrapArticleId === null) { return null; } } $document = $this->getDocumentInfoForAddPosition($doctype, $doctypeId); if ($document === null) { return null; } $giftWrapArticle = $this->getArticleInfoForAddPosition($document, $giftWrapArticleId); if ($giftWrapArticle === null) { return null; } $positionId = (int)$this->app->erp->AddPositionManuellPreisNummer( $doctype, $doctypeId, 0, $giftWrapArticle['nummer'], $quantity, $giftWrapArticle['name'], $price, '', 0, 0, $document['waehrung'] ); if (empty($positionId)) { return null; } if ($createOrderPosition) { $orderPositionId = (int)$this->app->erp->AddPositionManuellPreisNummer( 'auftrag', $orderId, 0, $giftWrapArticle['nummer'], $quantity, $giftWrapArticle['name'], $price, '', 0, 0, $document['waehrung'] ); $this->db->perform( 'UPDATE `rechnung_position` SET `auftrag_position_id` = :orderPositionId WHERE `id` = :id', [ 'orderPositionId' => $orderPositionId, 'id' => $positionId, ] ); } return $positionId; } /** * @param string $type * @param int $documentId * @param array $documentRows * @param array $positions * @param null|int $orderId */ private function addDiscountArticles($type, $documentId, $documentRows, $positions, $orderId = null): void { if (empty($documentId) || empty($documentRows) || empty($positions) || $documentId <= 0 || !in_array($type, ['invoice', 'returnorder'])) { return; } $doctype = $type === 'invoice' ? 'rechnung' : 'gutschrift'; foreach ($documentRows as $documentRow) { if (empty($documentRow['itemdiscount_price'])) { continue; } foreach ($documentRow['itemdiscount_price'] as $taxRate => $price) { if ($price == 0) { continue; } $positionId = (int)$this->addDiscountPosition($doctype, $documentId, $taxRate, $price); if ($orderId !== null) { $orderPositionId = (int)$this->addDiscountPosition('auftrag', $orderId, $taxRate, $price); $this->db->perform( 'UPDATE `rechnung_position` SET `auftrag_position_id` = :orderPositionId WHERE `id` = :id', [ 'orderPositionId' => $orderPositionId, 'id' => $positionId, ] ); } } } } /** * @param string $type * @param int $documentId * @param array $documentRows * @param array $positions * @param null|int $orderId */ private function addShippingArticles($type, $documentId, $documentRows, $positions, $orderId = null): void { if (empty($documentId) || empty($documentRows) || empty($positions) || $documentId <= 0 || !in_array($type, ['invoice', 'returnorder'])) { return; } $doctype = $type === 'invoice' ? 'rechnung' : 'gutschrift'; foreach ($documentRows as $documentRow) { if (empty($documentRow['shipping_price_with_discount'])) { continue; } foreach ($documentRow['shipping_price_with_discount'] as $taxRate => $price) { if ($price == 0) { continue; } $positionId = (int)$this->addShippingPosition( $doctype, $documentId, $taxRate, $price / (1 + $taxRate / 100) ); if ($orderId !== null) { $orderPositionId = (int)$this->addShippingPosition( 'auftrag', $orderId, $taxRate, $price / (1 + $taxRate / 100) ); $this->db->perform( 'UPDATE `rechnung_position` SET `auftrag_position_id` = :orderPositionId WHERE `id` = :id', [ 'orderPositionId' => $orderPositionId, 'id' => $positionId, ] ); } } } } /** * @param string $sku * * @return int */ private function getShopIdFromExternalNumber(string $sku): int { return (int)$this->db->fetchValue( "SELECT af.shopid FROM `artikelnummer_fremdnummern` AS `af` INNER JOIN `shopexport` AS `s` ON af.shopid = s.id AND s.aktiv = 1 AND s.modulename LIKE 'shopimporter_amazon%' WHERE af.aktiv = 1 AND af.nummer = :nummer AND af.nummer <> '' LIMIT 1", ['nummer' => trim($sku)] ); } /** * @param string $type * @param int $documentId * @param array $positions * @param null|int $orderId */ private function addPositions($type, $documentId, $positions, $orderId = null): void { if ($type === 'invoice') { $this->db->perform( 'DELETE FROM `rechnung_position` WHERE `rechnung` = :invoice', ['invoice' => $documentId] ); foreach ($positions as $key => $position) { if ($position['quantity'] == 0) { $position['quantity'] = 1; } $dataWithExternalNumber = null; $shopId = $this->getShopIdFromExternalNumber((string)$position['sku']); if ($shopId > 0) { $dataWithExternalNumber = ['fremdnummer' => trim($position['sku'])]; } $positionId = (int)$this->app->erp->AddPositionManuellPreisNummer( 'rechnung', $documentId, 0, $position['sku'], $position['quantity'], !isset($position['name']) ? $position['productname'] : $position['name'], $position['taxrate'] > 0 ? $position['itemprice'] / $position['quantity'] / (1 + $position['taxrate'] / 100) : $position['itemprice_netto'] / $position['quantity'], '', 0, $shopId, $position['currency'], $dataWithExternalNumber ); $this->db->perform( 'UPDATE `rechnung_position` SET `steuersatz` = :taxRate, preis = :price WHERE `id` = :id', [ 'taxRate' => $position['taxrate'], 'price' => $position['taxrate'] > 0 ? $position['itemprice'] / $position['quantity'] / (1 + $position['taxrate'] / 100) : $position['itemprice_netto'] / $position['quantity'], 'id' => $positionId, ] ); if ($orderId !== null) { $orderPositionId = (int)$this->app->erp->AddPositionManuellPreisNummer( 'auftrag', $orderId, 0, $position['sku'], $position['quantity'], !isset($position['name']) ? $position['productname'] : $position['name'], $position['taxrate'] > 0 ? $position['itemprice'] / $position['quantity'] / (1 + $position['taxrate'] / 100) : $position['itemprice_netto'] / $position['quantity'], '', 0, $shopId, $position['currency'], $dataWithExternalNumber ); $this->db->perform( 'UPDATE `auftrag_position` SET `steuersatz` = :taxRate, preis = :price WHERE `id` = :id', [ 'taxRate' => $position['taxrate'], 'price' => $position['taxrate'] > 0 ? $position['itemprice'] / $position['quantity'] / (1 + $position['taxrate'] / 100) : $position['itemprice_netto'] / $position['quantity'], 'id' => $orderPositionId, ] ); $this->db->perform( 'UPDATE `rechnung_position` SET `auftrag_position_id` = :orderPositionId WHERE `id` = :id', [ 'orderPositionId' => $orderPositionId, 'id' => $positionId, ] ); } if (!empty($position['id'])) { $this->db->perform( 'UPDATE `amazoninvoice_position` SET `doctype_id` = :doctype_id, `doctype` = :doctype, `position_id` = :position_id WHERE `id` = :id', [ 'doctype_id' => (int)$documentId, 'doctype' => 'rechnung', 'position_id' => (int)$positionId, 'id' => (int)$position['id'], ] ); } if ( !empty($position['giftwrap_price_net_with_discount']) && $position['giftwrap_price_net_with_discount'] != 0.0 ) { $this->addGiftWrapPositon( 'rechnung', (int)$documentId, $position['quantity'], $position['giftwrap_price_net_with_discount'], $orderId ); } } } else { $this->db->perform( 'DELETE FROM `gutschrift_position` WHERE `gutschrift` = :returnorder', ['returnorder' => $documentId] ); foreach ($positions as $key => $position) { if ($position['quantity'] == 0) { $position['quantity'] = 1; } $dataWithExternalNumber = null; $shopId = $this->getShopIdFromExternalNumber((string)$position['sku']); if ($shopId > 0) { $dataWithExternalNumber = ['fremdnummer' => trim($position['sku'])]; } $positionId = $this->app->erp->AddPositionManuellPreisNummer( 'gutschrift', $documentId, 0, $position['sku'], $position['quantity'], $position['name'], $position['price'], '', 0, $shopId, $position['currency'], $dataWithExternalNumber ); $this->db->perform( 'UPDATE `gutschrift_position` SET `steuersatz` = :taxRate, preis = :price WHERE `id` = :id', [ 'taxRate' => $position['taxrate'], 'price' => $position['taxrate'] > 0 ? $position['bruttototal'] / $position['quantity'] / (1 + $position['taxrate'] / 100) : $position['nettototal'] / $position['quantity'], 'id' => $positionId, ] ); if (!empty($position['id'])) { $this->db->perform( 'UPDATE `amazoninvoice_position` SET `doctype_id` = :doctype_id, `doctype` = :doctype, `position_id` = :position_id WHERE `id` = :id', [ 'doctype_id' => (int)$documentId, 'doctype' => 'gutschrift', 'position_id' => (int)$positionId, 'id' => (int)$position['id'], ] ); } if ( !empty($position['giftwrap_price_net_with_discount']) && $position['giftwrap_price_net_with_discount'] != 0.0 ) { $this->addGiftWrapPositon( 'gutschrift', (int)$documentId, $position['quantity'], $position['giftwrap_price_net_with_discount'] ); } } } } /** * @param string $type * @param int $documentId * @param string $file */ private function archiveDocument($type, $documentId, $file): void { if ($type === 'invoice') { $Brief = new RechnungPDF($this->app); $table = 'rechnung'; } else { $Brief = new GutschriftPDF($this->app); $table = 'gutschrift'; } $file = $this->app->erp->GetTMP() . $file; if (!is_file($file)) { throw new InvalidArgumentException(sprintf('file %s not found', $file)); } $list = $Brief->getArchivedFiles($documentId, $table); if (!empty($list)) { foreach ($list as $pdfArchive) { if (empty($pdfArchive['schreibschutz'])) { continue; } $dir = $this->app->Conf->WFuserdata . '/pdfarchiv/' . $this->app->Conf->WFdbname . '/' . $table; $pdfFile = Briefpapier::getPDFfolder($dir, $documentId, $pdfArchive['dateiname']); if (@rename($file, $pdfFile)) { $this->db->perform( 'UPDATE `pdfarchiv` SET `checksum` = :checksum WHERE `id` = :id', ['checksum' => md5_file($pdfFile), 'id' => $pdfArchive['id']] ); return; } throw new InvalidArgumentException(sprintf('could not write file %s', $pdfFile)); } } } }