Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon;
use Xentral\Core\DependencyInjection\ContainerInterface;
use Xentral\Modules\ShopimporterAmazon\Service\AmazonDocumentService;
use Xentral\Modules\ShopimporterAmazon\Service\InvoiceUploadDocumentService;
use Xentral\Modules\ShopimporterAmazon\Service\InvoiceUploadQueueService;
final class Bootstrap
{
/**
* @return array
*/
public static function registerServices(): array
{
return [
AmazonDocumentService::class => 'onInitAmazonDocumentService',
InvoiceUploadDocumentService::class => 'onInitInvoiceUploadDocumentService',
InvoiceUploadQueueService::class => 'onInitInvoiceUploadQueueService',
];
}
/**
* @param ContainerInterface $container
*
* @return AmazonDocumentService
*/
public static function onInitAmazonDocumentService(ContainerInterface $container): AmazonDocumentService
{
return new AmazonDocumentService($container->get('Database'));
}
/**
* @param ContainerInterface $container
*
* @return InvoiceUploadDocumentService
*/
public static function onInitInvoiceUploadDocumentService(ContainerInterface $container
): InvoiceUploadDocumentService {
return new InvoiceUploadDocumentService($container->get('Database'));
}
/**
* @param ContainerInterface $container
*
* @return InvoiceUploadQueueService
*/
public static function onInitInvoiceUploadQueueService(ContainerInterface $container): InvoiceUploadQueueService
{
return new InvoiceUploadQueueService($container->get('Database'));
}
}
@@ -0,0 +1,627 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Data;
use DateTimeInterface;
use DateTime;
class InvoiceUpload
{
private const SENT_AT_NULL_THRESHOLD = '1970-01-02 00:00:00';
/** @var int|null $id */
private $id;
/** @var int $shopId */
private $shopId;
/** @var int $internalOrderId */
private $internalOrderId;
/** @var int $invoiceId */
private $invoiceId;
/** @var int $creditNoteId */
private $creditNoteId;
/** @var string $orderId */
private $orderId;
/** @var string $shippingId */
private $shippingId;
/** @var string $status */
private $status;
/** @var DateTimeInterface|null $createdAt */
private $createdAt;
/** @var DateTimeInterface|null $sentAt */
private $sentAt;
/** @var string $report */
private $report;
/** @var string $marketplace */
private $marketplace;
/** @var float $totalAmount */
private $totalAmount;
/** @var float $totalVatAmount */
private $totalVatAmount;
/** @var string $transactionId */
private $transactionId;
/** @var int $countSent */
private $countSent;
/** @var int|null $fileId */
private $fileId;
/** @var string $invoiceNumber */
private $invoiceNumber;
/** @var string $errorCode */
private $errorCode;
/** @var string $errorMessage */
private $errorMessage;
/**
* InvoiceUpload constructor.
*
* @param int $shopId
* @param int $internalOrderId
* @param string $orderId
* @param string $shippingId
* @param string $transactionId
* @param string $marketplace
* @param string $invoiceNumber
* @param float $totalAmount
* @param float $totalVatAmount
* @param int $invoiceId
* @param int $creditNoteId
* @param DateTimeInterface|null $createdAt
* @param string $status
* @param string $report
* @param string $errorCode
* @param string $errorMessage
* @param int $fileId
* @param int $countSent
* @param DateTimeInterface|null $sentAt
* @param int|null $id
*/
public function __construct(
int $shopId,
int $internalOrderId,
string $orderId,
string $shippingId,
string $transactionId,
string $marketplace,
string $invoiceNumber,
float $totalAmount,
float $totalVatAmount,
int $invoiceId,
int $creditNoteId = 0,
?DateTimeInterface $createdAt = null,
string $status = '',
string $report = '',
string $errorCode = '',
string $errorMessage = '',
int $fileId = 0,
int $countSent = 0,
?DateTimeInterface $sentAt = null,
?int $id = null
) {
$this->shopId = $shopId;
$this->internalOrderId = $internalOrderId;
$this->orderId = $orderId;
$this->shippingId = $shippingId;
$this->transactionId = $transactionId;
$this->marketplace = $marketplace;
$this->invoiceNumber = $invoiceNumber;
$this->totalAmount = $totalAmount;
$this->totalVatAmount = $totalVatAmount;
$this->invoiceId = $invoiceId;
$this->creditNoteId = $creditNoteId;
$this->status = $status;
$this->report = $report;
$this->errorCode = $errorCode;
$this->errorMessage = $errorMessage;
$this->fileId = $fileId;
$this->countSent = $countSent;
$this->id = $id;
$this->createdAt = $createdAt;
$this->setSentAt($sentAt);
}
/**
* @param array $dbState
*
* @return static
*/
public static function fromDbState(array $dbState): self
{
$createdAt = $dbState['created_at'] === '0000-00-00 00:00:00' || $dbState['created_at'] === null
? '' : $dbState['created_at'];
$sentAt = $dbState['sent_at'] === '0000-00-00 00:00:00' || $dbState['sent_at'] === null
? '' : $dbState['sent_at'];
return new self(
(int)$dbState['shop_id'],
(int)$dbState['int_order_id'],
(string)$dbState['orderid'],
(string)$dbState['shippingid'],
(string)$dbState['transaction_id'],
(string)$dbState['marketplace'],
(string)$dbState['invoice_number'],
(float)$dbState['total_amount'],
(float)$dbState['total_vat_amount'],
(int)$dbState['invoice_id'],
(int)$dbState['credit_note_id'],
DateTime::createFromFormat('Y-m-d H:i:s', $createdAt) ?: null,
(string)$dbState['status'],
(string)$dbState['report'],
(string)$dbState['error_code'],
(string)$dbState['error_message'],
(int)$dbState['file_id'],
(int)$dbState['count_sent'],
DateTime::createFromFormat('Y-m-d H:i:s', $sentAt) ?: null,
empty($dbState['id']) ? null : (int)$dbState['id']
);
}
/**
* @return array
*/
public function toArray(): array
{
return [
'id' => $this->id,
'shop_id' => $this->shopId,
'int_order_id' => $this->internalOrderId,
'invoice_id' => $this->invoiceId,
'file_id' => $this->fileId,
'orderid' => $this->orderId,
'shippingid' => $this->shippingId,
'created_at' => $this->createdAt === null ? null : $this->createdAt->format('Y-m-d H:i:s'),
'sent_at' => $this->sentAt === null ? null : $this->sentAt->format('Y-m-d H:i:s'),
'report' => $this->report,
'marketplace' => $this->marketplace,
'status' => $this->status,
'error_code' => $this->errorCode,
'error_message' => $this->errorMessage,
'invoice_number' => $this->invoiceNumber,
'total_amount' => $this->totalAmount,
'total_vat_amount' => $this->totalVatAmount,
'credit_note_id' => $this->creditNoteId,
'transaction_id' => $this->transactionId,
'count_sent' => $this->countSent,
];
}
/**
* @return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* @param int|null $id
*
* @return self
*/
public function setId(?int $id): self
{
$this->id = $id;
return $this;
}
/**
* @return int
*/
public function getShopId(): int
{
return $this->shopId;
}
/**
* @param int $shopId
*
* @return self
*/
public function setShopId(int $shopId): self
{
$this->shopId = $shopId;
return $this;
}
/**
* id of table auftrag
*
* @return int
*/
public function getInternalOrderId(): int
{
return $this->internalOrderId;
}
/**
* @param int $internalOrderId
*
* @return self
*/
public function setInternalOrderId(int $internalOrderId): self
{
$this->internalOrderId = $internalOrderId;
return $this;
}
/**
* @return int
*/
public function getInvoiceId(): int
{
return $this->invoiceId;
}
/**
* @param int $invoiceId
*
* @return self
*/
public function setInvoiceId(int $invoiceId): self
{
$this->invoiceId = $invoiceId;
return $this;
}
/**
* @return int
*/
public function getCreditNoteId(): int
{
return $this->creditNoteId;
}
/**
* @param int $creditNoteId
*
* @return self
*/
public function setCreditNoteId(int $creditNoteId): self
{
$this->creditNoteId = $creditNoteId;
return $this;
}
/**
* column internet in table auftrag (order-number from Amazon)"
*
* @return string
*/
public function getExternalOrderId(): string
{
return $this->orderId;
}
/**
* @param string $orderId
*
* @return self
*/
public function setExternalOrderId(string $orderId): self
{
$this->orderId = $orderId;
return $this;
}
/**
* @return string
*/
public function getShippingId(): string
{
return $this->shippingId;
}
/**
* @param string $shippingId
*
* @return self
*/
public function setShippingId(string $shippingId): self
{
$this->shippingId = $shippingId;
return $this;
}
/**
* @return string
*/
public function getStatus(): string
{
return $this->status;
}
/**
* @param string $status
*
* @return self
*/
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
/**
* @return DateTimeInterface|null
*/
public function getCreatedAt(): ?DateTimeInterface
{
return $this->createdAt;
}
/**
* @param DateTimeInterface|null $createdAt
*
* @return self
*/
public function setCreatedAt(?DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
/**
* @return DateTimeInterface|null
*/
public function getSentAt(): ?DateTimeInterface
{
return $this->sentAt;
}
/**
* @param DateTimeInterface|null $sentAt
*
* @return self
*/
public function setSentAt(?DateTimeInterface $sentAt): self
{
if ($sentAt === null || $sentAt <= new DateTime(self::SENT_AT_NULL_THRESHOLD)) {
$this->sentAt = null;
return $this;
}
$this->sentAt = $sentAt;
return $this;
}
/**
* @return string
*/
public function getReport(): string
{
return $this->report;
}
/**
* @param string $report
*
* @return self
*/
public function setReport(string $report): self
{
$this->report = $report;
return $this;
}
/**
* @return string
*/
public function getMarketplace(): string
{
return $this->marketplace;
}
/**
* @param string $marketplace
*
* @return self
*/
public function setMarketplace(string $marketplace): self
{
$this->marketplace = $marketplace;
return $this;
}
/**
* @return float
*/
public function getTotalAmount(): float
{
return $this->totalAmount;
}
/**
* @param float $totalAmount
*
* @return self
*/
public function setTotalAmount(float $totalAmount): self
{
$this->totalAmount = $totalAmount;
return $this;
}
/**
* @return float
*/
public function getTotalVatAmount(): float
{
return $this->totalVatAmount;
}
/**
* @param float $totalVatAmount
*
* @return self
*/
public function setTotalVatAmount(float $totalVatAmount): self
{
$this->totalVatAmount = $totalVatAmount;
return $this;
}
/**
* @return string
*/
public function getTransactionId(): string
{
return $this->transactionId;
}
/**
* @param string $transactionId
*
* @return self
*/
public function setTransactionId(string $transactionId): self
{
$this->transactionId = $transactionId;
return $this;
}
/**
* @return int
*/
public function getCountSent(): int
{
return $this->countSent;
}
/**
* @param int $countSent
*
* @return self
*/
public function setCountSent(int $countSent): self
{
$this->countSent = $countSent;
return $this;
}
/**
* @param int $incrementation
*
* @return $this
*/
public function increaseCountSent(int $incrementation = 1): self
{
$this->countSent += $incrementation;
return $this;
}
/**
* @return int|null
*/
public function getFileId(): ?int
{
return $this->fileId;
}
/**
* @param int|null $fileId
*
* @return self
*/
public function setFileId(?int $fileId): self
{
$this->fileId = $fileId;
return $this;
}
/**
* @return string
*/
public function getInvoiceNumber(): string
{
return $this->invoiceNumber;
}
/**
* @param string $invoiceNumber
*
* @return self
*/
public function setInvoiceNumber(string $invoiceNumber): self
{
$this->invoiceNumber = $invoiceNumber;
return $this;
}
/**
* @return string
*/
public function getErrorCode(): string
{
return $this->errorCode;
}
/**
* @param string $errorCode
*
* @return self
*/
public function setErrorCode(string $errorCode): self
{
$this->errorCode = $errorCode;
return $this;
}
/**
* @return string
*/
public function getErrorMessage(): string
{
return $this->errorMessage;
}
/**
* @param string $errorMessage
*
* @return self
*/
public function setErrorMessage(string $errorMessage): self
{
$this->errorMessage = $errorMessage;
return $this;
}
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class AmazonBadRequestException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class AmazonNotReachableException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class AmazonServiceDeactivatedException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class AuthenticationException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use InvalidArgumentException as SplInvalidArgumentException;
class InvalidArgumentException extends SplInvalidArgumentException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class InvoiceUploadNotFoundException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use Xentral\Core\Exception\ModuleExceptionInterface;
interface ShopimporterAmazonExceptionInterface extends ModuleExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class SignatureServiceNotReachableException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Exception;
use RuntimeException as SplRuntimeException;
class ThrottlingException extends SplRuntimeException implements ShopimporterAmazonExceptionInterface
{
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Service;
interface AmazonDocumentInterface
{
public function getArticleBySku(int $orderId, string $sku, string $itemId = ''): ?int;
public function getPositionsFromOrderId(int $orderId, ?string $itemId = null): ?array;
public function getShippingArticleIdsByShopId(int $shopId): ?array;
public function getShippingAmountInCreditNotes(int $invoiceId, array $shippingArticleIds): float;
public function getArticleQuantityInCreditNotes(int $invoiceId, int $articleId): float;
public function getArticleQuantityInOrder(int $orderId, int $articleId): float;
public function getShippingAmountInOrder(int $orderId, array $shippingArticleIds): float;
public function getInvoicesByOrderId(int $orderId): array;
public function getOrderByExtId(string $extId): array;
public function getCreditNoteIdByInvoiceIds(array $invoiceIds, ?string $documentDate = null): ?int;
public function getCreditNotesByArticlesAndInvoiceIds(int $articleId, array $invoiceIds): array;
}
@@ -0,0 +1,305 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Service;
use Xentral\Components\Database\Database;
final class AmazonDocumentService implements AmazonDocumentInterface
{
/** @var Database $db */
private $db;
/**
* AmazonDocumentService constructor.
*
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* @param int $orderId
* @param string $sku
* @param string $itemId
*
* @return int|null
*/
public function getArticleBySku(int $orderId, string $sku, string $itemId = ''): ?int
{
if (!empty($itemId)) {
$articleId = (int)$this->db->fetchValue(
'SELECT `artikel` FROM `auftrag_position` WHERE `webid` = :webid AND `auftrag` = :order_id LIMIT 1',
[
'webid' => $itemId,
'order_id' => $orderId,
]
);
if ($articleId > 0) {
return $articleId;
}
}
if (empty($sku)) {
return null;
}
$articleId = (int)$this->db->fetchValue(
"SELECT af.artikel
FROM `artikelnummer_fremdnummern` AS af
INNER JOIN artikel AS art ON af.artikel = art.id AND art.geloescht <> 1
WHERE af.nummer <> '' AND af.nummer = :sku AND af.aktiv = 1
LIMIT 1",
['sku' => $sku]
);
if ($articleId > 0) {
return $articleId;
}
$articleId = (int)$this->db->fetchValue(
"SELECT art.id
FROM artikel AS art
WHERE art.geloescht <> 1 AND art.nummer = :sku AND art.nummer <> '' AND art.nummer <> 'DEL'
LIMIT 1",
['sku' => $sku]
);
return $articleId > 0 ? $articleId : null;
}
/**
* @param int $orderId
* @param string|null $itemId
*
* @return array|null
*/
public function getPositionsFromOrderId(int $orderId, ?string $itemId = null): ?array
{
if (!empty($itemId)) {
return $this->db->fetchAll(
'SELECT *
FROM `auftrag_position`
WHERE `auftrag` = :order_id
ORDER BY `webid` = :webid DESC, `sort`, `id`
LIMIT 1',
[
'order_id' => $orderId,
'webid' => $itemId,
]
);
}
return $this->db->fetchAll(
'SELECT *
FROM `auftrag_position`
WHERE `auftrag` = :order_id
ORDER BY `sort`, `id`
LIMIT 1',
[
'order_id' => $orderId,
]
);
}
/**
* @param int $shopId
*
* @return array|null
*/
public function getShippingArticleIdsByShopId(int $shopId): ?array
{
$shopExport = $this->db->fetchRow(
'SELECT `artikelportoermaessigt`, `artikelporto` FROM `shopexport` WHERE `id` = :shop_id',
['shop_id' => $shopId]
);
if (empty($shopExport)) {
return null;
}
$articleIds = [];
if (!empty($shopExport['artikelportoermaessigt'])) {
$articleIds[] = (int)$shopExport['artikelportoermaessigt'];
}
if (!empty($shopExport['artikelporto'])) {
$articleIds[] = (int)$shopExport['artikelporto'];
}
return empty($articleIds) ? null : array_unique($articleIds);
}
/**
* @param int $invoiceId
* @param array $shippingArticleIds
*
* @return float
*/
public function getShippingAmountInCreditNotes(int $invoiceId, array $shippingArticleIds): float
{
return (float)$this->db->fetchValue(
"SELECT SUM(cnp.preis * cnp.menge)
FROM `gutschrift` AS `cn`
INNER JOIN `gutschrift_position` AS `cnp` ON cn.id = cnp.gutschrift
INNER JOIN `artikel` AS `art` ON cnp.artikel = art.id
WHERE cn.rechnungid = :invoice_id AND cn.rechnungid != 0 AND cn.status <> 'storniert'
AND (art.porto = 1 OR art.id IN (:shipping_article_ids))",
[
'invoice_id' => $invoiceId,
'shipping_article_ids' => $shippingArticleIds,
]
);
}
/**
* @param int $invoiceId
* @param int $articleId
*
* @return float
*/
public function getArticleQuantityInCreditNotes(int $invoiceId, int $articleId): float
{
return (float)$this->db->fetchValue(
"SELECT SUM(gspos.menge)
FROM `gutschrift` AS `gs`
INNER JOIN `gutschrift_position` AS `gspos` ON gs.id = gspos.gutschrift AND gspos.artikel = :article_id
WHERE gs.rechnungid = :invoice_id AND gs.rechnungid != 0 AND gs.status <> 'storniert'",
[
'invoice_id' => $invoiceId,
'article_id' => $articleId,
]
);
}
/**
* @param int $orderId
* @param int $articleId
*
* @return float
*/
public function getArticleQuantityInOrder(int $orderId, int $articleId): float
{
return (float)$this->db->fetchValue(
"SELECT SUM(op.menge)
FROM `auftrag` AS `o`
INNER JOIN `auftrag_position` AS `op` ON o.id = op.auftrag AND op.artikel = :article_id
WHERE o.id = :order_id",
[
'order_id' => $orderId,
'article_id' => $articleId,
]
);
}
/**
* @param int $orderId
* @param array $shippingArticleIds
*
* @return float
*/
public function getShippingAmountInOrder(int $orderId, array $shippingArticleIds): float
{
return (float)$this->db->fetchValue(
"SELECT SUM(op.preis * op.menge)
FROM `auftrag` AS `o`
INNER JOIN `auftrag_position` AS `op` ON o.id = op.auftrag
INNER JOIN `artikel` AS `art` ON op.artikel = art.id
WHERE o.id = :order_id AND (art.porto = 1 OR art.id IN (:shipping_article_ids))",
[
'order_id' => $orderId,
'shipping_article_ids' => $shippingArticleIds,
]
);
}
/**
* @param int $orderId
*
* @return array
*/
public function getInvoicesByOrderId(int $orderId): array
{
return $this->db->fetchRow(
"SELECT *
FROM `rechnung`
WHERE `auftragid` = :order_id AND `status` <> 'angelegt'
ORDER BY `status` = 'storniert'",
['order_id' => $orderId]
);
}
/**
* @param string $extId
*
* @return array
*/
public function getOrderByExtId(string $extId): array
{
return $this->db->fetchRow(
"SELECT *
FROM `auftrag`
WHERE `shopextid` = :ext_id AND `shopextid` <> '' AND `shopextid` IS NOT NULL AND `status` <> 'storniert'
LIMIT 1",
['ext_id' => $extId]
);
}
/**
* @param array $invoiceIds
* @param string|null $documentDate
*
* @return int|null
*/
public function getCreditNoteIdByInvoiceIds(array $invoiceIds, ?string $documentDate = null): ?int
{
if (!empty($documentDate)) {
$creditNoteId = $this->db->fetchValue(
"SELECT gs.id
FROM `gutschrift` AS `gs`
WHERE gs.rechnungid IN (:invoice_ids) AND gs.rechnungid <> 0 AND gs.rechnungid <> ''
AND (gs.datum = CURDATE() OR gs.datum = :document_date)
ORDER BY gs.datum = :document_date DESC
LIMIT 1",
[
'invoice_ids' => $invoiceIds,
'document_date' => $documentDate,
]
);
} else {
$creditNoteId = $this->db->fetchValue(
'SELECT gs.id
FROM `gutschrift` AS `gs`
WHERE gs.rechnungid IN (:invoice_ids) AND gs.rechnungid <> 0 AND gs.rechnungid <> \'\'
AND gs.datum = CURDATE()
LIMIT 1',
[
'invoice_ids' => $invoiceIds,
]
);
}
return $creditNoteId === false ? null : (int)$creditNoteId;
}
/**
* @param int $articleId
* @param array $invoiceIds
*
* @return array
*/
public function getCreditNotesByArticlesAndInvoiceIds(int $articleId, array $invoiceIds): array
{
return $this->db->fetchPairs(
'SELECT gs.id, gs.rechnungid
FROM `gutschrift` AS `gs`
INNER JOIN `gutschrift_position` AS `gspos` ON gs.id = gspos.gutschrift AND gspos.artikel = :article_id
WHERE gs.rechnungid IN (:invoice_ids) AND gs.rechnungid!=0 AND gs.rechnungid != 0
LIMIT 1',
[
'article_id' => $articleId,
'invoice_ids' => $invoiceIds,
]
);
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Service;
use Xentral\Modules\ShopimporterAmazon\Data\InvoiceUpload;
interface InvoiceUploadDocumentInterface
{
/**
* @param InvoiceUpload $invoiceUpload
*
* @return int
*/
public function create(InvoiceUpload $invoiceUpload): int;
/**
* @param InvoiceUpload $invoiceUpload
*/
public function update(InvoiceUpload $invoiceUpload): void;
}
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Service;
use Exception;
use Xentral\Components\Database\Database;
use Xentral\Modules\ShopimporterAmazon\Data\InvoiceUpload;
use Xentral\Modules\ShopimporterAmazon\Exception\InvalidArgumentException;
use Xentral\Modules\ShopimporterAmazon\Exception\InvoiceUploadNotFoundException;
final class InvoiceUploadDocumentService implements InvoiceUploadDocumentInterface
{
/** @var Database $db */
private $db;
/**
* InvoiceUploadDocumentService constructor.
*
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* @param int $id
*
* @throws InvoiceUploadNotFoundException
*
* @return InvoiceUpload
*/
public function getById(int $id): InvoiceUpload
{
$dbState = $this->db->fetchRow(
"SELECT *
FROM `shopimporter_amazon_invoice_upload`
WHERE `id` = :id ",
[
'id' => $id,
]
);
if (empty($dbState)) {
throw new InvoiceUploadNotFoundException("invoiceUpload with Id {$id} not found");
}
return InvoiceUpload::fromDbState($dbState);
}
/**
* @param InvoiceUpload $invoiceUpload
*
* @return int
*/
public function create(InvoiceUpload $invoiceUpload): int
{
if ($invoiceUpload->getId() !== null) {
throw new InvalidArgumentException('InvoiceUpload-object has already an database-assignment');
}
$query = $this->db->insert()
->into('shopimporter_amazon_invoice_upload')
->cols(
$invoiceUpload->toArray()
);
$this->db->perform(
$query->getStatement(),
$query->getBindValues()
);
return $this->db->lastInsertId();
}
/**
* @param InvoiceUpload $invoiceUpload
*/
public function update(InvoiceUpload $invoiceUpload): void
{
if ($invoiceUpload->getId() === null) {
throw new InvalidArgumentException('InvoiceUpload-object has no database assignment');
}
$query = $this->db->update()
->table('shopimporter_amazon_invoice_upload')
->where('id=:id')
->bindValue('id', $invoiceUpload->getId())
->cols($invoiceUpload->toArray());
$this->db->perform(
$query->getStatement(),
$query->getBindValues()
);
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Service;
use DateTimeInterface;
use Xentral\Modules\ShopimporterAmazon\Data\InvoiceUpload;
interface InvoiceUploadQueueInterface
{
/**
* get next Invoice Request to Invoice-information and PDF to Amazon. This has to be sent in 3 seconds interval
*
* @param int $shopId
* @param DateTimeInterface $startDate
*
* @return InvoiceUpload|null
*/
public function getNextInvoiceUploadRequest(int $shopId, DateTimeInterface $startDate): ?InvoiceUpload;
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\ShopimporterAmazon\Service;
use DateTimeInterface;
use Exception;
use Xentral\Components\Database\Database;
use Xentral\Modules\ShopimporterAmazon\Data\InvoiceUpload;
class InvoiceUploadQueueService implements InvoiceUploadQueueInterface
{
/** @var Database $db */
private $db;
/**
* AmazonDocumentService constructor.
*
* @param Database $db
*/
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* get next Invoice Request to Invoice-information and PDF to Amazon. This has to be sent in 3 seconds interval
*
* @param int $shopId
* @param DateTimeInterface $startDate
*
* @return InvoiceUpload|null
*/
public function getNextInvoiceUploadRequest(int $shopId, DateTimeInterface $startDate): ?InvoiceUpload
{
$dbState = $this->db->fetchRow(
"SELECT saiu.*
FROM `shopimporter_amazon_invoice_upload` AS `saiu`
INNER JOIN `rechnung` AS `i` ON saiu.invoice_id = i.id
AND i.datum >= :start_date
WHERE ( saiu.sent_at IS NULL OR saiu.sent_at <= :sent_at OR saiu.status = '')
AND saiu.shop_id = :shop_id AND saiu.status <> 'error' AND saiu.marketplace <> ''
AND saiu.invoice_id > 0
ORDER BY saiu.created_at
LIMIT 1 ",
[
'shop_id' => $shopId,
'start_date' => $startDate->format('Y-m-d'),
'sent_at' => '1970-01-02 00:00:00'
]
);
if (empty($dbState)) {
return null;
}
return InvoiceUpload::fromDbState($dbState);
}
}
@@ -0,0 +1,20 @@
img.amazonimageprev {
border:0;
max-width:50px;
max-height:50px;
}
table.sendarticlepopuptable > tbody >tr {
vertical-align: bottom;
}
table#newoffertable tbody tr + tr td:nth-child(1),
table#searchresults tbody tr + tr td:nth-child(2) {
text-align: center;
text-transform: uppercase;
font-weight: bold;
}
div#searchresultsdiv {
max-height: 250px;
overflow-y: auto;
}
@@ -0,0 +1,44 @@
var ShopImporterAmazon = function ($) {
'use strict';
var me = {
storage: {
shopId: null,
invoiceUploadIds: null
},
init: function() {
$('#resetinvoices').on('click', function() {
me.storage.invoiceUploadIds = [];
$('#shopimporter_amazon_invoice_upload').find(':checked').each(function(){
me.storage.invoiceUploadIds.push($(this).data('id'));
});
if(me.storage.invoiceUploadIds.length > 0) {
$.ajax({
url: 'index.php?module=onlineshops&action=edit&id='+
$('#resetinvoices').data('shopid') +'&cmd=resetinvoiceuploads',
type: 'POST',
dataType: 'json',
data: {
invoiceUploadIds: me.storage.invoiceUploadIds,
},
success: function() {
$('#shopimporter_amazon_invoice_upload').DataTable().ajax.reload();
}
});
}
});
$('#sellectallinvoices').on('change', function(){
$('#shopimporter_amazon_invoice_upload').find('input').prop('checked', $(this).prop('checked'));
});
}
};
return {
init: me.init
};
}(jQuery);
$(document).ready(function () {
ShopImporterAmazon.init();
});
@@ -0,0 +1,245 @@
var ShopImporterAmazonAttachedOffers = function ($) {
'use strict';
var me = {
storage: {
shopId: null,
articleInputValue: null
},
selector: {
attachedOffersTable: '#shopimporter_amazon_attachedoffers',
newOfferTable: '#newoffertable',
popupAttached: '#popupattatch',
popupArticle: '#popuparticle',
popupArticleInfo: '#popuparticleinfo',
},
reloadAttachedOffersTable: function()
{
$(me.selector.attachedOffersTable).DataTable().ajax.reload();
},
addEmptyArticleWarning: function() {
if($(me.selector.popupArticle).val()+'' === '') {
$(me.selector.popupArticleInfo).html('Pflichtfeld!');
return true;
}
$(me.selector.popupArticleInfo).html('');
return false;
},
init: function() {
me.storage.shopId = $(me.selector.popupAttached).data('id');
$(me.selector.attachedOffersTable).on('afterreload',function(){
$('#shopimporter_amazon_attachedoffers img.delete').on('click',function(){
if(confirm('Wiklich löschen?')) {
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=attachedoffers&id='+
me.storage.shopId +'&cmd=delete',
type: 'POST',
dataType: 'json',
data: {
element: $(this).data('id')
},
success: function(data) {
me.reloadAttachedOffersTable();
},
beforeSend: function() {
}
});
}
});
});
$(me.selector.popupAttached).dialog(
{
modal: true,
autoOpen: false,
minWidth: 1440,
title:'',
buttons: {
'ERSTELLEN': function()
{
if(me.addEmptyArticleWarning()) {
return;
}
$('#frmnewoffer').trigger('submit');
},
'ABBRECHEN': function() {
$(this).dialog('close');
}
},
close: function(event, ui){
}
});
$('#new').on('click',function(){
$(me.selector.popupAttached).dialog('open');
});
$(me.selector.popupAttached).on('autocompleteclose',function(){
$(me.selector.popupAttached).trigger('change');
});
$(me.selector.popupAttached).on('autocompletechange',function(){
$(me.selector.popupAttached).trigger('change');
});
$(me.selector.popupArticle).on('change',function(){
me.addEmptyArticleWarning();
me.storage.articleInputValue = ($(this).val()+'').split(' ') [ 0 ];
if(me.storage.articleInputValue === '') {
return;
}
$('input.popupskufba').each(function(){
$(this).attr('placeholder', me.storage.articleInputValue+'_'+(this.id.split('_')[ 1 ]).toUpperCase()+'_FBA');
});
$('input.popupskufbm').each(function(){
$(this).attr('placeholder', me.storage.articleInputValue+'_'+(this.id.split('_')[ 1 ]).toUpperCase());
});
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=attachedoffers&id='+
me.storage.shopId +'&cmd=getean',
type: 'POST',
dataType: 'json',
data: {
article: $(me.selector.popupArticle).val()
},
success: function (data) {
if(typeof data.ean != 'undefined'
&& (
$('#popupasin').val()+'' === ''
|| data.articleid+'' !== $('#lastarticle').val()+''
)
) {
$('#searchtype').val('EAN');
$('#popupasin').val(data.ean);
$('#lastean').val(data.ean);
$('#lastarticle').val(data.id);
$('#searchasin').trigger('click');
$('.popuptitle').val('');
$('.ownprice').val('');
$(me.selector.newOfferTable).find('.trmarketplace').find('select').val('');
$(me.selector.newOfferTable).find('.trmarketplace').find('select.condition').val('New');
}
else {
$('#lastean').val('');
if(data.articleid+'' !== $('#lastarticle').val()+'') {
$('#lastarticle').val(data.articleid);
$('#popupasin').val('');
$('.popuptitle').val('');
$('.ownprice').val('');
$(me.selector.newOfferTable).find('.trmarketplace').find('select').val('');
$(me.selector.newOfferTable).find('.trmarketplace').find('select.condition').val('New');
}
if($('#searchtype').val() === 'EAN'
&& $('#popupasin').val()+'' === $('#lastean').val()+'') {
$('#popupasin').val('');
}
}
if(typeof data.prices != 'undefined') {
$.each(data.prices, function (key, value) {
if((key+'').length === 2) {
$('input[name="price_'+key+'"]').attr('placeholder', value);
}
})
}
if(typeof data.articleid != 'undefined') {
$('#lastarticle').val(data.articleid);
}
}
});
});
$('#popupasin').on('keypress',function(event){
if (event.which == 13) {
$('#searchasin').trigger('click');
}
});
$('#searchasin').on('click',function(){
var asin = $('#popupasin').val();
if(asin+'' === '') {
return;
}
$('#searchresultshead').nextAll('tr').remove();
$('#searchresultshead').toggleClass('hide', true);
$('#searchresultsdiv').loadingOverlay('show');
$(me.selector.popupAttached).find('tr.trmarketplace').each(function(){
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=attachedoffers&id='+
me.storage.shopId +'&cmd=getOffers',
type: 'POST',
dataType: 'json',
data: {
asin: $('#popupasin').val(),
searchtype: $('#searchtype').val(),
marketplace: $(this).data('marketplace')
},
success: function(data) {
$('#searchresultsdiv').loadingOverlay('remove');
var $tr = $(me.selector.popupAttached).find('tr.trmarketplace[data-marketplace="'+data.marketplace+'"]');
if($tr.length === 0) {
return;
}
if(data.status == 0) {
$($tr).hide();
$($tr).find('input').val('');
$($tr).find('.prices').html('');
}
else {
$($tr).show();
$($tr).find('.prices').html(data.prices);
$($tr).find('.popuptitle').val(data.title);
$($tr).find('.curreny').val(data.currency);
}
if(typeof data.tr != 'undefined') {
$('#searchresultshead').toggleClass('hide', false);
$('#searchresultshead').after(data.tr);
}
$('#searchresults span.childrenasin').off('click');
$('#searchresults span.parentasin').off('click');
$('#searchresults img.useasin').off('click');
$('#searchresults span.childrenasin').on('click', function(){
$('#popupasin').val($(this).data('asin'));
$('#searchtype').val('ASIN');
$('#searchasin').trigger('click');
});
$('#searchresults span.parentasin').on('click', function(){
$('#popupasin').val($(this).data('asin'));
$('#searchtype').val('ASIN');
$('#searchasin').trigger('click');
});
$('#searchresults img.useasin').on('click', function(){
if($($(this).parents('tr').find('td.asin')).html()+'' != '') {
$('#popupasin').val($($(this).parents('tr').find('td.asin')).html()+'');
$('#searchtype').val('ASIN');
$('#searchasin').trigger('click');
return;
}
var $tr = $('tr.trmarketplace[data-marketplace="'+$(this).data('marketplace')+'"]');
if($tr.length) {
$($tr).show();
$($tr).find('.popuptitle').val($($(this).parents('tr').find('td.title')).html());
$($tr).find('.popupasin').val($($(this).parents('tr').find('td.asin')).html());
}
});
if($('#searchtype').val()==='ASIN') {
$('input.popupasin').val($('#popupasin').val());
}
if($(me.selector.popupArticle).val()+'' !== '') {
$(me.selector.popupArticle).trigger('change');
}
},
beforeSend: function() {
}
});
});
});
}
};
return {
init: me.init
};
}(jQuery);
$(document).ready(function () {
ShopImporterAmazonAttachedOffers.init();
});
@@ -0,0 +1,293 @@
var ShopImporterAmazonSendArticles = function ($) {
'use strict';
var me = {
storage: {
templateTarget: null
},
selector: {
flatFileTable: '#shopimporter_amazon_flatfile',
articlePopup: '#getarticlediv',
articlePopupForm: '#getarticlefrm',
templatePopup: '#popupTemplate',
flatFileTemplateInfo: '#flatFileTemplateInfo',
deleteIcon: 'img.deletearticle',
getIcon: 'img.getarticle'
},
reloadFlatFileTable: function () {
$(me.selector.flatFileTable).DataTable().ajax.reload();
},
updateAutoComplete: function () {
$(me.selector.articlePopupForm + ' input').each(function () {
if (typeof this.id != 'undefined') {
if (this.id === 'article') {
$(this).autocomplete({
source: 'index.php?module=ajax&action=filter&filtername=artikelnummer',
select: function (event, ui) {
var i = ui.item.value;
var zahl = i.indexOf(' ');
var text = i.slice(0, zahl);
$('input#article').val(text);
return false;
}
});
} else if (typeof this.id != 'undefined') {
$(this).autocomplete({
source: 'index.php?module=ajax&action=filter&filtername=amazongetarticle&flatfile='
+ encodeURI($('#flatfile').val())
+ '&feedproducttype='
+ encodeURI($('#feed_product_type').val())
+ '&elementid='
+ this.id
});
}
}
});
},
deleteArticle: function (id) {
if (!confirm('Wirklich löschen?')) {
return;
}
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=sendarticles&cmd=deletearticle',
type: 'POST',
dataType: 'json',
data: {
article_ids: id
},
success: function () {
me.reloadFlatFileTable();
},
beforeSend: function () {
}
});
},
createImportTemplate: function (template)
{
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=sendarticles&cmd=createImportTemplate',
type: 'POST',
dataType: 'json',
data: {
template: template,
},
success: function (data) {
if(typeof data.id != 'undefined') {
$(me.selector.flatFileTemplateInfo).html(
'Importvorlage: <a href="index.php?module=importvorlage&action=edit&id='
+data.id+'" target="_blank">'+data.bezeichnung+'</a>'
);
}
}
});
},
createExportTemplate: function (template)
{
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=sendarticles&cmd=createExportTemplate',
type: 'POST',
dataType: 'json',
data: {
template: template,
},
success: function (data) {
if(typeof data.id != 'undefined') {
$(me.selector.flatFileTemplateInfo).html(
'Exportvorlage: <a href="index.php?module=exportvorlage&action=edit&id='
+data.id+'" target="_blank">'+data.bezeichnung+'</a>'
);
}
}
});
},
getArticle: function (id) {
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=sendarticles&cmd=getarticle',
type: 'POST',
dataType: 'json',
data: {
article_id: id,
flatfile_id: $('#flatfile').val(),
shopid: $(me.selector.articlePopup).data('shopid')
},
success: function (data) {
$(me.selector.articlePopupForm).html(data.html);
me.updateAutoComplete();
$(me.selector.articlePopupForm + ' input[data-required]').on('change',
function () {
if (trim($(this).val() + '') === ''
&&
(typeof $(this).attr('placeholder') == 'undefined'
|| trim($(this).attr('placeholder') + '') === '')
) {
$(this).next('span').remove();
$(this).after('<span class="red">Pflichtfeld</span>');
} else {
$(this).next('span').remove();
}
}
);
$('#feed_product_type').on('change', function () {
me.updateAutoComplete();
});
$('#flatarticletabs').tabs();
$(me.selector.articlePopupForm + ' input[data-required]').trigger('change');
$('img.delprevimage').on('click', function () {
$('#' + $(this).data('field')).val('');
$('#' + $(this).data('field')).parents('tr').first().find('img.amazonimageprev').attr(
'src',
'./themes/new/images/keinbild_hell.png'
);
});
$('img.getprevimage').on('click', function () {
var val = ($('#amazonimgprevdiv').find(':checked').val()) + '';
if (val !== '') {
$('#' + $(this).data('field')).val(val);
$('#' + $(this).data('field')).parents('tr').first().find('img.amazonimageprev').attr(
'src',
'index.php?module=ajax&action=thumbnail&cmd=artikel&id=' + val
);
}
});
$(me.selector.articlePopup).dialog('open');
},
beforeSend: function () {
}
});
},
openTemplate: function(){
me.storage.templateTarget = 'articlePopup';
$(me.selector.flatFileTemplateInfo).html('');
$(me.selector.templatePopup).dialog('open');
},
init: function () {
if ($(me.selector.articlePopup).length === 0) {
return;
}
$(me.selector.articlePopup).dialog(
{
modal: true,
autoOpen: false,
minWidth: 1040,
title: '',
buttons: {
'SPEICHERN': function () {
$(me.selector.articlePopupForm + ' input[data-required]').trigger('change');
if ($(me.selector.articlePopupForm + ' span.red').length
&& !confirm('Es sind nicht alle Pflichtfelder ausgewählt wirklich speicher?')) {
return;
}
$(me.selector.articlePopupForm).trigger('submit');
},
ABBRECHEN: function () {
$(this).dialog('close');
}
},
close: function (event, ui) {
}
});
$(me.selector.templatePopup).dialog({
modal: true,
autoOpen: false,
minWidth: 1040,
title: '',
buttons: {
'WEITER': function () {
if($('#flatfile').val()+'' !== '') {
if(me.storage.templateTarget === 'articlePopup') {
$(this).dialog('close');
me.getArticle(0);
}
if(me.storage.templateTarget === 'exportTemplate') {
me.createExportTemplate($('#flatfile').val());
}
if(me.storage.templateTarget === 'importTemplate') {
me.createImportTemplate($('#flatfile').val());
}
}
},
'ABBRECHEN': function () {
$(this).dialog('close');
}
},
close: function (event, ui) {
}
});
$('#new').on('click', function () {
me.openTemplate();
});
$('#newExportSendArticles').on('click', function (){
me.storage.templateTarget = 'exportTemplate';
$(me.selector.flatFileTemplateInfo).html('');
$(me.selector.templatePopup).dialog('open');
});
$('#newImportSendArticles').on('click', function (){
me.storage.templateTarget = 'importTemplate';
$(me.selector.flatFileTemplateInfo).html('');
$(me.selector.templatePopup).dialog('open');
});
$('#send').on('click', function () {
var data_ids = '';
$(me.selector.flatFileTable + ' input:checked').each(function () {
data_ids += ',' + $(this).data('id');
});
if (data_ids !== '') {
if ($('#selaction').val() === 'send') {
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=sendarticles&cmd=sendarticles',
type: 'POST',
dataType: 'json',
data: {
article_ids: data_ids
},
success: function (data) {
me.reloadFlatFileTable();
}
});
} else if ($('#selaction').val() === 'delete') {
if (!confirm('Wirklich löschen?')) {
return;
}
$.ajax({
url: 'index.php?module=shopimporter_amazon&action=sendarticles&cmd=deletearticle',
type: 'POST',
dataType: 'json',
data: {
article_ids: data_ids
},
success: function (data) {
me.reloadFlatFileTable();
}
});
}
} else {
alert('Sie haben keine Artikel ausgewählt');
}
});
$(me.selector.flatFileTable).on('afterreload', function () {
$(this).find(me.selector.getIcon).off('click');
$(this).find(me.selector.deleteIcon).off('click');
$(this).find(me.selector.getIcon).on('click', function () {
me.getArticle($(this).data('id'));
});
$(this).find(me.selector.deleteIcon).on('click', function () {
me.deleteArticle($(this).data('id'));
});
});
$(me.selector.flatFileTable).trigger('afterreload');
}
};
return {
init: me.init
};
}(jQuery);
$(document).ready(function () {
ShopImporterAmazonSendArticles.init();
});