Merge branch 'exciler_pr'

This commit is contained in:
OpenXE 2024-07-14 09:56:20 +02:00
commit 612483b5e7
67 changed files with 19651 additions and 13946 deletions

1
.gitignore vendored
View File

@ -2,4 +2,5 @@ conf/user.inc.php
conf/user_defined.php
userdata
www/cache/
node_modules/
www/themes/new/css/custom.css

View File

@ -1,4 +1,10 @@
<?php
/*
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
namespace Xentral\Components\Http;
@ -9,8 +15,8 @@ class JsonResponse extends Response
{
/**
* @param array|JsonSerializable $data
* @param int $statusCode
* @param array $headers
* @param int $statusCode
* @param array $headers
*/
public function __construct($data = [], $statusCode = self::HTTP_OK, array $headers = [])
{
@ -29,4 +35,19 @@ class JsonResponse extends Response
parent::__construct($content, $statusCode, $headers);
}
public static function NoContent(array $headers = []): JsonResponse
{
return new JsonResponse([], Response::HTTP_NO_CONTENT, $headers);
}
public static function BadRequest(array|JsonSerializable $data = [], array $headers = []): JsonResponse
{
return new JsonResponse($data, Response::HTTP_BAD_REQUEST, $headers);
}
public static function NotFound(array|JsonSerializable $data = [], array $headers = []): JsonResponse
{
return new JsonResponse($data, Response::HTTP_NOT_FOUND, $headers);
}
}

View File

@ -1,4 +1,9 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
namespace Xentral\Components\Http;
@ -488,6 +493,16 @@ class Request
return !empty($this->content) ? $this->content : '';
}
/**
* Return a JSON request body as php object
*
* @return object
*/
public function getJson() : object
{
return json_decode($this->getContent());
}
/**
* Returns the content type of the request.
*

View File

@ -377,11 +377,13 @@ class ShopimportController
$stats['packages_yesterday'] = $verkaufszahlen->getPackages(
" v.versendet_am=DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 day),'%Y-%m-%d') '",
sprintf('INNER JOIN `auftrag` AS `a` ON l.auftragid = a.id AND a.shop = %d', $shopId)
sprintf('INNER JOIN `auftrag` AS `a` ON l.auftragid = a.id AND a.shop = %d', $shopId),
false
);
$stats['packages_today'] = $verkaufszahlen->getPackages(
" v.versendet_am=DATE_FORMAT(NOW(),'%Y-%m-%d') '",
sprintf('INNER JOIN `auftrag` AS `a` ON l.auftragid = a.id AND a.shop = %d', $shopId)
sprintf('INNER JOIN `auftrag` AS `a` ON l.auftragid = a.id AND a.shop = %d', $shopId),
false
);
[

View File

@ -3,6 +3,7 @@
namespace Xentral\Modules\Article;
use Xentral\Core\DependencyInjection\ContainerInterface;
use Xentral\Modules\Article\Service\ArticleService;
use Xentral\Modules\Article\Service\SellingPriceService;
use Xentral\Modules\Article\Gateway\ArticleGateway;
use Xentral\Modules\Article\Service\CurrencyConversionService;
@ -17,6 +18,7 @@ final class Bootstrap
{
return [
'ArticleGateway' => 'onInitArticleGateway',
'ArticleService' => 'onInitArticleService',
'PurchasePriceService' => 'onInitPurchasePriceService',
'SellingPriceService' => 'onInitSellingPriceService',
'CurrencyConversionService' => 'onInitCurrencyConversionService',
@ -65,4 +67,17 @@ final class Bootstrap
return new CurrencyConversionService($app->erp);
}
/**
* @param ContainerInterface $container
*
* @return ArticleService
*/
public static function onInitArticleService(ContainerInterface $container)
{
/** @var \ApplicationCore $app */
$app = $container->get('LegacyApplication');
return new ArticleService($app);
}
}

View File

@ -1,4 +1,9 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-FileCopyrightText: 2019 Xentral (c) Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg, Germany
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace Xentral\Modules\Article\Gateway;
@ -346,4 +351,13 @@ final class ArticleGateway
);
}
}
public function SetVariantStatus(int $articleId, ?int $variantOfId) : void {
$sql = "UPDATE artikel SET variante = :isVariant, variante_von = :variantOfId WHERE id = :articleId";
$this->db->perform($sql, [
'articleId' => $articleId,
'variantOfId' => $variantOfId > 0 ? $variantOfId : null,
'isVariant' => $variantOfId > 0
]);
}
}

View File

@ -0,0 +1,131 @@
<?php
namespace Xentral\Modules\Article\Service;
use ApplicationCore;
use Xentral\Components\Database\Database;
class ArticleService
{
private ApplicationCore $app;
public function __construct(ApplicationCore $app)
{
$this->app = $app;
}
function CopyArticle(int $id, bool $purchasePrices, bool $sellingPrices, bool $files, bool $properties,
bool $instructions, bool $partLists, bool $customFields, string $newArticleNumber = '')
{
$this->app->DB->MysqlCopyRow('artikel','id',$id);
$idnew = $this->app->DB->GetInsertID();
$steuersatz = $this->app->DB->Select("SELECT steuersatz FROM artikel WHERE id = '$id' LIMIT 1");
if($steuersatz == ''){
$steuersatz = -1.00;
$this->app->DB->Update("UPDATE artikel SET steuersatz = '$steuersatz' WHERE id = '$idnew' LIMIT 1");
}
$this->app->DB->Update("UPDATE artikel SET nummer='$newArticleNumber' WHERE id='$idnew' LIMIT 1");
if($this->app->DB->Select("SELECT variante_kopie FROM artikel WHERE id = '$id' LIMIT 1"))
$this->app->DB->Update("UPDATE artikel SET variante = 1, variante_von = '$id' WHERE id = '$idnew' LIMIT 1");
if($partLists){
// wenn stueckliste
$stueckliste = $this->app->DB->Select("SELECT stueckliste FROM artikel WHERE id='$id' LIMIT 1");
if($stueckliste==1)
{
$artikelarr = $this->app->DB->SelectArr("SELECT * FROM stueckliste WHERE stuecklistevonartikel='$id'");
$cartikelarr = $artikelarr?count($artikelarr):0;
for($i=0;$i<$cartikelarr;$i++)
{
$sort = $artikelarr[$i]['sort'];
$artikel = $artikelarr[$i]['artikel'];
$referenz = $artikelarr[$i]['referenz'];
$place = $artikelarr[$i]['place'];
$layer = $artikelarr[$i]['layer'];
$stuecklistevonartikel = $idnew;
$menge = $artikelarr[$i]['menge'];
$firma = $artikelarr[$i]['firma'];
$this->app->DB->Insert("INSERT INTO stueckliste (id,sort,artikel,referenz,place,layer,stuecklistevonartikel,menge,firma) VALUES
('','$sort','$artikel','$referenz','$place','$layer','$stuecklistevonartikel','$menge','$firma')");
}
}
}
if($purchasePrices){
$einkaufspreise = $this->app->DB->SelectArr("SELECT id FROM einkaufspreise WHERE artikel = '$id'");
if($einkaufspreise){
foreach($einkaufspreise as $preis){
$neuereinkaufspreis = $this->app->DB->MysqlCopyRow("einkaufspreise", "id", $preis['id']);
$this->app->DB->Update("UPDATE einkaufspreise SET artikel = '$idnew' WHERE id = '$neuereinkaufspreis' LIMIT 1");
}
}
}
if($sellingPrices){
$verkaufspreise = $this->app->DB->SelectArr("SELECT id FROM verkaufspreise WHERE artikel = '$id'");
if($verkaufspreise){
foreach($verkaufspreise as $preis){
$neuerverkaufspreis = $this->app->DB->MysqlCopyRow("verkaufspreise", "id", $preis['id']);
$this->app->DB->Update("UPDATE verkaufspreise SET artikel = '$idnew' WHERE id = '$neuerverkaufspreis' LIMIT 1");
}
}
}
if($files){
$dateien = $this->app->DB->SelectArr("SELECT DISTINCT datei FROM datei_stichwoerter WHERE parameter = '$id' AND objekt = 'Artikel'");
$datei_stichwoerter = $this->app->DB->SelectArr("SELECT id,datei FROM datei_stichwoerter WHERE parameter = '$id' AND objekt = 'Artikel'");
if($dateien){
foreach($dateien as $datei){
$titel = $this->app->DB->Select("SELECT titel FROM datei WHERE id='".$datei['datei']."' LIMIT 1");
$beschreibung = $this->app->DB->Select("SELECT beschreibung FROM datei WHERE id='".$datei['datei']."' LIMIT 1");
$nummer = $this->app->DB->Select("SELECT nummer FROM datei WHERE id='".$datei['datei']."' LIMIT 1");
$name = $this->app->DB->Select("SELECT dateiname FROM datei_version WHERE datei='".$this->app->DB->real_escape_string($datei['datei'])."' ORDER by version DESC LIMIT 1");
$ersteller = $this->app->User->GetName();
$tmpnewdateiid = $this->app->erp->CreateDatei($name,$titel,$beschreibung,$nummer,$this->app->erp->GetDateiPfad($datei['datei']),$ersteller);
$datei_mapping[$datei['datei']] = $tmpnewdateiid;
}
}
if($datei_stichwoerter){
foreach($datei_stichwoerter as $datei){
$neuesstichwort = $this->app->DB->MysqlCopyRow("datei_stichwoerter", "id", $datei['id']);
$newdatei = $datei_mapping[$datei['datei']];
$this->app->DB->Update("UPDATE datei_stichwoerter SET datei='$newdatei', parameter = '$idnew', objekt = 'Artikel' WHERE id = '$neuesstichwort' LIMIT 1");
}
}
}
if($properties){
$aeigenschaften = $this->app->DB->SelectArr("SELECT id FROM artikeleigenschaftenwerte WHERE artikel = '$id'");
if($aeigenschaften){
foreach($aeigenschaften as $eigenschaft){
$neue_eigenschaft = $this->app->DB->MysqlCopyRow("artikeleigenschaftenwerte", "id", $eigenschaft['id']);
$this->app->DB->Update("UPDATE artikeleigenschaftenwerte SET artikel = '$idnew' WHERE id = '$neue_eigenschaft' LIMIT 1");
}
}
}
if($instructions){
$arbeitsanweisungen = $this->app->DB->SelectArr("SELECT id FROM artikel_arbeitsanweisung WHERE artikel = '$id'");
if($arbeitsanweisungen){
foreach($arbeitsanweisungen as $anweisung){
$neue_anweisung = $this->app->DB->MysqlCopyRow("artikel_arbeitsanweisung", "id", $anweisung['id']);
$this->app->DB->Update("UPDATE artikel_arbeitsanweisung SET artikel = '$idnew' WHERE id = '$neue_anweisung' LIMIT 1");
}
}
}
if($customFields){
$freifelderuebersetzungen = $this->app->DB->SelectArr("SELECT id FROM artikel_freifelder WHERE artikel = '$id'");
if($freifelderuebersetzungen){
$this->app->DB->Insert("INSERT INTO artikel_freifelder (artikel, sprache, nummer, wert) SELECT '$idnew', sprache, nummer, wert FROM artikel_freifelder WHERE artikel = '$id'");
}
}
return $idnew;
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
namespace Xentral\Modules\MatrixProduct;
use Xentral\Core\DependencyInjection\ContainerInterface;
final class Bootstrap
{
/**
* @return array
*/
public static function registerServices()
{
return [
'MatrixProductService' => 'onInitMatrixProductService',
'MatrixProductGateway' => 'onInitMatrixProductGateway',
];
}
/**
* @param ContainerInterface $container
*
* @return MatrixProductService
*/
public static function onInitMatrixProductService(ContainerInterface $container) : MatrixProductService
{
return new MatrixProductService(
$container->get('Database'),
$container->get('MatrixProductGateway'),
$container->get('ArticleGateway')
);
}
/**
* @param ContainerInterface $container
*
* @return MatrixProductGateway
*/
public static function onInitMatrixProductGateway(ContainerInterface $container) : MatrixProductGateway
{
return new MatrixProductGateway($container->get('Database'));
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace Xentral\Modules\MatrixProduct\Data;
use JsonSerializable;
final class Group implements JsonSerializable
{
public function __construct(
public string $name,
public ?int $id = null,
public bool $active = true,
public ?string $nameExternal = null,
public int $projectId = 0,
public bool $required = false,
public ?int $articleId = null,
public int $sort = 0
)
{ }
public static function fromDbArray(array $data) : self {
return new self($data['name'], $data['id'], $data['aktiv'], $data['name_ext'], $data['projekt'], $data['pflicht'],
$data['artikel'] ?? null, $data['sort'] ?? 0);
}
public function jsonSerialize(): array
{
return (array) $this;
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace Xentral\Modules\MatrixProduct\Data;
use JsonSerializable;
final class Option implements JsonSerializable
{
public function __construct(
public string $name,
public readonly int $groupId,
public ?int $id = null,
public bool $active = true,
public string $nameExternal = '',
public int $sort = 0,
public string $articleNumber = '',
public string $articleNumberSuffix = '',
public readonly ?int $globalOptionId = null,
public readonly ?int $articleId = null
)
{
}
public static function fromDbArray(array $data) : Option {
return new self($data['name'], $data['gruppe'], $data['id'], $data['aktiv'], $data['name_ext'], $data['sort'],
$data['artikelnummer'], $data['articlenumber_suffix'], $data['matrixprodukt_eigenschaftenoptionen'] ?? null,
$data['artikel'] ?? null);
}
public function jsonSerialize(): array
{
return (array) $this;
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace Xentral\Modules\MatrixProduct\Data;
use JsonSerializable;
final class Translation implements JsonSerializable
{
public function __construct(
public string $nameFrom,
public string $languageTo,
public string $nameTo,
public ?int $id = null,
public string $nameExternalFrom = '',
public string $nameExternalTo = '',
public string $languageFrom = 'DE'
)
{
}
public static function fromDbArray(array $data): Translation {
return new self($data['name_from'], $data['language_to'], $data['name_to'], $data['id'],
$data['name_external_from'], $data['name_external_to'], $data['language_from']);
}
public function jsonSerialize(): array
{
return (array) $this;
}
}

View File

@ -0,0 +1,350 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
namespace Xentral\Modules\MatrixProduct;
use Xentral\Components\Database\Database;
use Xentral\Modules\MatrixProduct\Data\Group;
use Xentral\Modules\MatrixProduct\Data\Option;
use Xentral\Modules\MatrixProduct\Data\Translation;
final class MatrixProductGateway
{
/**
* @param Database $db
*/
public function __construct(private readonly Database $db)
{
}
//region Groups
public function GetGlobalGroupById(int $id) : Group {
$sql = "SELECT * FROM matrixprodukt_eigenschaftengruppen WHERE id = :id";
$res = $this->db->fetchRow($sql, ['id' => $id]);
return Group::fromDbArray($res);
}
public function GetArticleGroupById(int $id) : Group {
$sql = "SELECT * FROM matrixprodukt_eigenschaftengruppen_artikel WHERE id = :id";
$res = $this->db->fetchRow($sql, ['id' => $id]);
return Group::fromDbArray($res);
}
public function GetArticleGroupIdByName(int $articleId, string $name) : int {
$sql = "SELECT id FROM matrixprodukt_eigenschaftengruppen_artikel
WHERE name = :name AND artikel = :articleId";
return $this->db->fetchValue($sql, [
'name' => $name,
'articleId' => $articleId
]);
}
public function GetArticleGroupsByArticleId(int $articleId) : array {
$sql = "SELECT * FROM matrixprodukt_eigenschaftengruppen_artikel WHERE artikel = :articleId";
$rows = $this->db->fetchAssoc($sql, ['articleId' => $articleId]);
$res = [];
foreach ($rows as $row) {
$res[] = Group::fromDbArray($row);
}
return $res;
}
public function InsertGlobalGroup(Group $obj) : Group {
$sql = "INSERT INTO matrixprodukt_eigenschaftengruppen
(aktiv, name, name_ext, projekt, pflicht)
VALUES
(:active, :name, :nameExternal, :projectId, :required)";
print_r($obj);
$this->db->perform($sql, (array)$obj);
$obj->id = $this->db->lastInsertId();
return $obj;
}
public function InsertArticleGroup(Group $obj) : Group {
$sql = "INSERT INTO matrixprodukt_eigenschaftengruppen_artikel
(artikel, aktiv, name, name_ext, projekt, sort, pflicht)
VALUES
(:articleId, :active, :name, :nameExternal, :projectId, :sort, :required)";
$this->db->perform($sql, (array)$obj);
$obj->id = $this->db->lastInsertId();
return $obj;
}
public function UpdateGlobalGroup(Group $obj) : Group {
$sql = "UPDATE matrixprodukt_eigenschaftengruppen
SET aktiv = :active, name = :name, name_ext = :nameExternal, projekt = :projectId, pflicht = :required
WHERE id = :id";
$this->db->perform($sql, (array)$obj);
return $obj;
}
public function UpdateArticleGroup(Group $obj) : Group {
$sql = "UPDATE matrixprodukt_eigenschaftengruppen_artikel
SET aktiv = :active, name = :name, name_ext = :nameExternal, projekt = :projectId, sort = :sort, pflicht = :required
WHERE id = :id";
$this->db->perform($sql, (array)$obj);
return $obj;
}
public function DeleteGlobalGroup(int $id) : void {
$sql = "UPDATE matrixprodukt_eigenschaftenoptionen_artikel moa
JOIN matrixprodukt_eigenschaftenoptionen mo ON moa.matrixprodukt_eigenschaftenoptionen=mo.id
JOIN matrixprodukt_eigenschaftengruppen mg ON mo.gruppe=mg.id
SET moa.matrixprodukt_eigenschaftenoptionen=0
WHERE mg.id = :id";
$this->db->perform($sql, ['id' => $id]);
$sql = "DELETE mg, mo
FROM matrixprodukt_eigenschaftengruppen mg
LEFT OUTER JOIN matrixprodukt_eigenschaftenoptionen mo ON mo.gruppe = mg.id
WHERE mg.id = :id";
$this->db->perform($sql, ['id' => $id]);
}
public function DeleteArticleGroup(int $id) : void {
$sql = "DELETE mga, moa, mota
FROM matrixprodukt_eigenschaftengruppen_artikel mga
LEFT OUTER JOIN matrixprodukt_eigenschaftenoptionen_artikel moa ON moa.gruppe = mga.id
LEFT OUTER JOIN matrixprodukt_optionen_zu_artikel mota ON mota.option_id = moa.id
WHERE mga.id = :id";
$this->db->perform($sql, ['id' => $id]);
}
//endregion
//region Options
public function GetGlobalOptionById(int $id) : Option {
$sql = "SELECT * FROM matrixprodukt_eigenschaftenoptionen WHERE id = :id";
$res = $this->db->fetchRow($sql, ['id' => $id]);
return Option::fromDbArray($res);
}
public function GetArticleOptionById(int $id) : Option {
$sql = "SELECT * FROM matrixprodukt_eigenschaftenoptionen_artikel WHERE id = :id";
$res = $this->db->fetchRow($sql, ['id' => $id]);
return Option::fromDbArray($res);
}
public function GetArticleOptionIdsByGroupIds(int|array $groupIds) : array {
if (empty($groupIds))
return [];
$sql = "SELECT id FROM matrixprodukt_eigenschaftenoptionen WHERE gruppe IN (:ids)";
return $this->db->fetchCol($sql, ['ids' => $groupIds]);
}
public function GetArticleOptionIdByName(int $articleId, int $groupId, string $name) : int {
$sql = "SELECT id FROM matrixprodukt_eigenschaftenoptionen_artikel
WHERE name = :name AND artikel = :articleId AND gruppe = :groupId";
return $this->db->fetchValue($sql, [
'name' => $name,
'articleId' => $articleId,
'groupId' => $groupId
]);
}
public function GetArticleOptionsByArticleId(int $articleId) : array {
$sql = "SELECT * FROM matrixprodukt_eigenschaftenoptionen_artikel WHERE artikel = :articleId";
$rows = $this->db->fetchAssoc($sql, ['articleId' => $articleId]);
$res = [];
foreach ($rows as $row) {
$res[] = Option::fromDbArray($row);
}
return $res;
}
public function GetSelectedOptionIdsByVariantId(int $variantId) : array {
$sql = "SELECT option_id FROM matrixprodukt_optionen_zu_artikel WHERE artikel = :variantId";
return $this->db->fetchCol($sql, ['variantId' => $variantId]);
}
public function InsertGlobalOption(Option $obj) : Option {
$sql = "INSERT INTO matrixprodukt_eigenschaftenoptionen
(aktiv, name, name_ext, sort, gruppe, artikelnummer, articlenumber_suffix)
VALUES
(:active, :name, :nameExternal, :sort, :groupId, :articleNumber, :articleNumberSuffix)";
$this->db->perform($sql, (array)$obj);
$obj->id = $this->db->lastInsertId();
return $obj;
}
public function InsertArticleOption(Option $obj) : Option {
$sql = "INSERT INTO matrixprodukt_eigenschaftenoptionen_artikel
(artikel, matrixprodukt_eigenschaftenoptionen, aktiv, name, name_ext, sort, gruppe, artikelnummer, articlenumber_suffix)
VALUES
(:articleId, :globalOptionId, :active, :name, :nameExternal, :sort, :groupId, :articleNumber, :articleNumberSuffix)";
$this->db->perform($sql, (array)$obj);
$obj->id = $this->db->lastInsertId();
return $obj;
}
public function UpdateGlobalOption(Option $obj) : Option {
$sql = "UPDATE matrixprodukt_eigenschaftenoptionen
SET aktiv = :active, name = :name, name_ext = :nameExternal, sort = :sort, artikelnummer = :articleNumber,
articlenumber_suffix = :articleNumberSuffix
WHERE id = :id";
$this->db->perform($sql, (array)$obj);
return $obj;
}
public function UpdateArticleOption(Option $obj) : Option {
$sql = "UPDATE matrixprodukt_eigenschaftenoptionen_artikel
SET aktiv = :active, name = :name, name_ext = :nameExternal, sort = :sort, artikelnummer = :articleNumber,
articlenumber_suffix = :articleNumberSuffix
WHERE id = :id";
$this->db->perform($sql, (array)$obj);
return $obj;
}
public function DeleteGlobalOption(int $id) : void {
$sql = "UPDATE matrixprodukt_eigenschaftenoptionen_artikel moa
JOIN matrixprodukt_eigenschaftenoptionen mo ON moa.matrixprodukt_eigenschaftenoptionen=mo.id
SET moa.matrixprodukt_eigenschaftenoptionen=0
WHERE mo.id = :id";
$this->db->perform($sql, ['id' => $id]);
$sql = "DELETE moa
FROM matrixprodukt_eigenschaftenoptionen mo
WHERE mo.id = :id";
$this->db->perform($sql, ['id' => $id]);
}
public function DeleteArticleOption(int $id) : void {
$sql = "DELETE moa, mota
FROM matrixprodukt_eigenschaftenoptionen_artikel moa
LEFT OUTER JOIN matrixprodukt_optionen_zu_artikel mota ON mota.option_id=moa.id
WHERE moa.id = :id";
$this->db->perform($sql, ['id' => $id]);
}
//endregion
//region Variants
public function ReplaceVariant(int $oldId, int $newId) : void {
$sql = "UPDATE matrixprodukt_optionen_zu_artikel SET artikel = :newId WHERE artikel = :oldId";
$this->db->perform($sql, [
'oldId' => $oldId,
'newId' => $newId]
);
}
/**
* @param int $variantId
* @return int[]
*/
public function GetOptionIdsByVariant(int $variantId) : array {
$sql = "SELECT option_id FROM matrixprodukt_optionen_zu_artikel WHERE artikel = :id";
return $this->db->fetchCol($sql, ['id' => $variantId]);
}
public function AddOptionToVariant(int $variantId, int $optionId) : void {
$sql = "INSERT INTO matrixprodukt_optionen_zu_artikel (artikel, option_id) VALUES (:variantId, :optionId)";
$this->db->perform($sql, [
'variantId' => $variantId,
'optionId' => $optionId
]);
}
public function DeleteOptionFromVariant(int $variantId, int $optionId) : void {
$sql = "DELETE FROM matrixprodukt_optionen_zu_artikel WHERE artikel = :variantId AND option_id = :optionId";
$this->db->perform($sql, [
'variantId' => $variantId,
'optionId' => $optionId
]);
}
public function GetVariantIdByOptions(array $optionIds) : ?int {
if (empty($optionIds))
return null;
sort($optionIds);
$sql = "SELECT artikel
FROM matrixprodukt_optionen_zu_artikel
WHERE option_id IN (:ids)
GROUP BY artikel
HAVING group_concat(option_id order by option_id separator ',') = :idList";
$res = $this->db->fetchValue($sql, [
'ids' => $optionIds,
'idList' => join(',', $optionIds)
]);
return $res ?: null;
}
public function GetVariantIdsByOptions(int|array $optionIds) : array
{
if (empty($optionIds))
return [];
$sql = "SELECT artikel FROM matrixprodukt_optionen_zu_artikel WHERE option_id IN (:ids)";
return $this->db->fetchCol($sql, ['ids' => $optionIds]);
}
public function DeleteVariantById(int $variantId) : void {
$sql = "DELETE FROM matrixprodukt_optionen_zu_artikel WHERE artikel = :id";
$this->db->perform($sql, ['id' => $variantId]);
}
public function GetSuffixStringForOptionSet(array $optionIds) : string {
$sql = "SELECT GROUP_CONCAT(IFNULL(NULLIF(mao.articlenumber_suffix,''), mao.id) ORDER BY mag.sort, mag.id SEPARATOR '_')
FROM matrixprodukt_eigenschaftenoptionen_artikel mao
JOIN matrixprodukt_eigenschaftengruppen_artikel mag ON mao.gruppe = mag.id
WHERE mao.id IN (:idList)";
$res = $this->db->fetchValue($sql, ['idList' => $optionIds]);
return $res;
}
//endregion
//region Translations
public function GetGroupTranslationById(int $id) : Translation {
$sql = "SELECT * FROM matrix_article_translation WHERE id = :id";
$row = $this->db->fetchRow($sql, ['id' => $id]);
return Translation::fromDbArray($row);
}
public function GetOptionTranslationById(int $id) : Translation {
$sql = "SELECT * FROM matrix_article_options_translation WHERE id = :id";
$row = $this->db->fetchRow($sql, ['id' => $id]);
return Translation::fromDbArray($row);
}
public function InsertGroupTranslation(Translation $obj) : Translation {
$sql = "INSERT INTO matrix_article_translation
(language_from, language_to, name_from, name_to, name_external_from, name_external_to)
VALUES (:languageFrom, :languageTo, :nameFrom, :nameTo, :nameExternalFrom, :nameExternalTo)";
$this->db->perform($sql, (array)$obj);
$obj->id = $this->db->lastInsertId();
return $obj;
}
public function InsertOptionTranslation(Translation $obj) : Translation {
$sql = "INSERT INTO matrix_article_options_translation
(language_from, language_to, name_from, name_to, name_external_from, name_external_to)
VALUES (:languageFrom, :languageTo, :nameFrom, :nameTo, :nameExternalFrom, :nameExternalTo)";
$this->db->perform($sql, (array)$obj);
$obj->id = $this->db->lastInsertId();
return $obj;
}
public function UpdateGroupTranslation(Translation $obj) : Translation {
$sql = "UPDATE matrix_article_translation SET language_from = :languageFrom, language_to = :languageTo,
name_from = :nameFrom, name_to = :nameTo, name_external_from = :nameExternalFrom,
name_external_to = :nameExternalTo WHERE id = :id";
$this->db->perform($sql, (array)$obj);
return $obj;
}
public function UpdateOptionTranslation(Translation $obj) : Translation {
$sql = "UPDATE matrix_article_options_translation SET language_from = :languageFrom, language_to = :languageTo,
name_from = :nameFrom, name_to = :nameTo, name_external_from = :nameExternalFrom,
name_external_to = :nameExternalTo WHERE id = :id";
$this->db->perform($sql, (array)$obj);
return $obj;
}
public function DeleteGroupTranslation(int $id) : void {
$sql = "DELETE FROM matrix_article_translation WHERE id = :id";
$this->db->perform($sql, ['id' => $id]);
}
public function DeleteOptionTranslation(int $id) : void {
$sql = "DELETE FROM matrix_article_options_translation WHERE id = :id";
$this->db->perform($sql, ['id' => $id]);
}
//endregion
}

View File

@ -0,0 +1,209 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
namespace Xentral\Modules\MatrixProduct;
use Xentral\Components\Database\Database;
use Xentral\Modules\Article\Gateway\ArticleGateway;
use Xentral\Modules\MatrixProduct\Data\Option;
use Xentral\Modules\MatrixProduct\Data\Group;
use Xentral\Modules\MatrixProduct\Data\Translation;
final class MatrixProductService
{
/**
* @param Database $db
* @param MatrixProductGateway $gateway
* @param ArticleGateway $articleGateway
*/
public function __construct(
private readonly Database $db,
private readonly MatrixProductGateway $gateway,
private readonly ArticleGateway $articleGateway)
{ }
//region Groups
public function GetGlobalGroupById(int $id) : Group {
return $this->gateway->GetGlobalGroupById($id);
}
public function GetArticleGroupById(int $id) : Group {
return $this->gateway->GetArticleGroupById($id);
}
/**
* @param int $articleId
* @return Group[]
*/
public function GetArticleGroupsByArticleId(int $articleId) : array {
return $this->gateway->GetArticleGroupsByArticleId($articleId);
}
public function SaveGlobalGroup(Group $obj) : void {
if ($obj->id > 0)
$this->gateway->UpdateGlobalGroup($obj);
else
$this->gateway->InsertGlobalGroup($obj);
}
public function SaveArticleGroup(Group $obj) : void {
if ($obj->id > 0) {
$this->gateway->UpdateArticleGroup($obj);
} else {
$this->gateway->InsertArticleGroup($obj);
}
}
public function DeleteGlobalGroup(int $id) : void {
$this->gateway->DeleteGlobalGroup($id);
}
public function DeleteArticleGroup(int $id) : bool {
$options = $this->gateway->GetArticleOptionIdsByGroupIds($id);
$variants = $this->gateway->GetVariantIdsByOptions($options);
if (!empty($variants))
return false;
$this->gateway->DeleteArticleGroup($id);
return true;
}
//endregion
//region Options
public function GetGlobalOptionById(int $id) : Option {
return $this->gateway->GetGlobalOptionById($id);
}
public function GetArticleOptionById(int $id) : Option {
return $this->gateway->GetArticleOptionById($id);
}
/**
* @param int $articleId
* @return Option[]
*/
public function GetArticleOptionsByArticleId(int $articleId) : array {
return $this->gateway->GetArticleOptionsByArticleId($articleId);
}
public function GetSelectedOptionIdsByVariantId(int $variantId) : array {
return $this->gateway->GetSelectedOptionIdsByVariantId($variantId);
}
public function SaveGlobalOption(Option $obj) : void {
if ($obj->id > 0) {
$this->gateway->UpdateGlobalOption($obj);
} else {
$this->gateway->InsertGlobalOption($obj);
}
}
public function SaveArticleOption(Option $obj) : void {
if ($obj->id > 0) {
$this->gateway->UpdateArticleOption($obj);
} else {
$this->gateway->InsertArticleOption($obj);
}
}
public function DeleteGlobalOption(int $id) : void {
$this->gateway->DeleteGlobalOption($id);
}
public function DeleteArticleOption(int $id) : bool {
$variants = $this->gateway->GetVariantIdsByOptions($id);
if (!empty($variants))
return false;
$this->gateway->DeleteArticleOption($id);
return true;
}
public function AddGlobalOptionsForArticle(int $articleId, int|array $optionIds): void
{
$sql = "SELECT mg.name groupname, mg.name_ext groupnameext, mg.projekt as groupprojekt, mg.pflicht as grouprequired, mo.*
FROM matrixprodukt_eigenschaftenoptionen mo
JOIN matrixprodukt_eigenschaftengruppen mg on mo.gruppe=mg.id
WHERE mo.id IN (:optionIds)";
$optionArr = $this->db->fetchAll($sql, ['optionIds' => $optionIds]);
foreach ($optionArr as $option) {
$groupId = $this->gateway->GetArticleGroupIdByName($articleId, $option['groupname']);
if (!$groupId) {
$obj = new Group($option['groupname'], nameExternal: $option['groupnameext'], projectId: $option['groupprojekt'], required: $option['grouprequired'], articleId: $articleId);
$group = $this->gateway->InsertArticleGroup($obj);
$groupId = $group->id;
}
$optionId = $this->gateway->GetArticleOptionIdByName($articleId, $groupId, $option['name']);
if (!$optionId) {
$obj = new Option($option['name'], $groupId, nameExternal: $option['name_ext'], sort: $option['sort'], globalOptionId: $option['id'], articleId: $articleId);
$this->gateway->InsertArticleOption($obj);
}
}
}
//endregion
//region Variants
public function GetVariantIdByOptionSet(array $optionIds) : ?int {
return $this->gateway->GetVariantIdByOptions($optionIds);
}
public function GetSuffixStringForOptionSet(array $optionIds) : string
{
return $this->gateway->GetSuffixStringForOptionSet($optionIds);
}
public function SaveVariant(int $articleId, int $variantId, array $optionIds, ?int $oldVariantId = null) : bool|string {
if ($oldVariantId != null && $oldVariantId != $variantId) {
$this->gateway->ReplaceVariant($oldVariantId, $variantId);
$this->articleGateway->SetVariantStatus($oldVariantId, null);
}
$variantWithOptionSet = $this->gateway->GetVariantIdByOptions($optionIds);
if ($variantWithOptionSet != null && $variantWithOptionSet != $variantId)
return 'Diese Optionen wurden bereits einer anderen Variante zugewiesen';
$existingIds = $this->gateway->GetOptionIdsByVariant($variantId);
$toDelete = array_diff($existingIds, $optionIds);
$toCreate = array_diff($optionIds, $existingIds);
foreach ($toDelete as $item)
$this->gateway->DeleteOptionFromVariant($variantId, $item);
foreach ($toCreate as $item)
$this->gateway->AddOptionToVariant($variantId, $item);
$this->articleGateway->SetVariantStatus($variantId, $articleId);
return true;
}
public function DeleteVariant(int $variantId) : void {
$this->gateway->DeleteVariantById($variantId);
$this->articleGateway->SetVariantStatus($variantId, null);
}
//endregion
//region Translations
public function GetGroupTranslation(int $id) : Translation {
return $this->gateway->GetGroupTranslationById($id);
}
public function GetOptionTranslation(int $id) : Translation {
return $this->gateway->GetOptionTranslationById($id);
}
public function SaveGroupTranslation(Translation $obj) : Translation {
if ($obj->id > 0)
return $this->gateway->UpdateGroupTranslation($obj);
return $this->gateway->InsertGroupTranslation($obj);
}
public function SaveOptionTranslation(Translation $obj) : Translation {
if ($obj->id > 0)
return $this->gateway->UpdateOptionTranslation($obj);
return $this->gateway->InsertOptionTranslation($obj);
}
public function DeleteGroupTranslation(int $id) : void {
$this->gateway->DeleteGroupTranslation($id);
}
public function DeleteOptionTranslation(int $id) : void {
$this->gateway->DeleteOptionTranslation($id);
}
//endregion
}

View File

@ -0,0 +1,55 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import {ref, onMounted} from "vue";
import axios from "axios";
import Dialog from "primevue/dialog";
import Listbox from "primevue/listbox";
import Button from "primevue/button";
import {AlertErrorHandler} from '@res/js/ajaxErrorHandler';
const props = defineProps({
articleId: String
})
const emit = defineEmits(['save', 'close']);
const model = ref(null);
const group = ref(null);
const selected = ref([]);
onMounted(async () => {
model.value = await fetch('index.php?module=matrixprodukt&action=list&cmd=selectoptions')
.then(x => x.json())
})
async function save() {
await axios.post('index.php?module=matrixprodukt&action=artikel&cmd=addoptions', {
articleId: props.articleId,
optionIds: selected.value
})
.then(() => {emit('save')})
.catch(AlertErrorHandler);
}
</script>
<template>
<Dialog visible modal header="Globale Optionen hinzufügen" style="width: 500px" @update:visible="emit('close')">
<div v-if="model" class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProductOptions" style="padding-top: 5px;">Optionen:</label>
<Listbox multiple
:options="model"
optionGroupLabel="name"
optionGroupChildren="options"
optionLabel="name"
optionValue="id"
listStyle="height: 200px"
v-model="selected" />
</div>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="HINZUFÜGEN" @click="save" :disabled="selected.length === 0"/>
</template>
</Dialog>
</template>

View File

@ -0,0 +1,82 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import axios from "axios";
import {ref} from 'vue';
import {reloadDataTables} from "@res/js/jqueryBridge";
import AddGlobalToArticle from "./AddGlobalToArticle.vue";
import GroupEdit from "./GroupEdit.vue";
import OptionEdit from "./OptionEdit.vue";
import Variant from "./Variant.vue";
import Translation from "./Translation.vue";
import CreateMissing from "./CreateMissing.vue";
const model = ref(null);
document.getElementById('main').addEventListener('click', async (ev) => {
const target = ev.target;
if (!target || !target.classList.contains('vueAction'))
return;
const ds = target.dataset;
if (ds.action.endsWith('Delete')) {
const cnf = confirm('Wirklich löschen?');
if (!cnf)
return;
let url;
switch (ds.action) {
case 'groupDelete':
url = ds.articleId > 0
? 'index.php?module=matrixprodukt&action=artikel&cmd=groupdelete'
: 'index.php?module=matrixprodukt&action=list&cmd=delete';
await axios.post(url, {groupId: ds.groupId});
break;
case 'optionDelete':
url = ds.articleId > 0
? 'index.php?module=matrixprodukt&action=artikel&cmd=optiondelete'
: 'index.php?module=matrixprodukt&action=optionenlist&cmd=delete';
await axios.post(url, {optionId: ds.optionId});
break;
case 'variantDelete':
url = 'index.php?module=matrixprodukt&action=artikel&cmd=variantdelete';
await axios.post(url, {variantId: ds.variantId});
break;
case 'translationDelete':
url = 'index.php?module=matrixprodukt&action=translation&cmd=delete';
await axios.post(url, {id: ds.id, type: ds.type});
break;
}
onSave();
return;
}
model.value = ds;
});
function onSave() {
reloadDataTables();
onClose();
}
function onGroupSave() {
location.reload();
}
function onClose() {
model.value = null;
}
</script>
<template>
<template v-if="model">
<AddGlobalToArticle v-if="model.action === 'addGlobalToArticle'" v-bind="model" @close="onClose" @save="onGroupSave" />
<GroupEdit v-else-if="model.action === 'groupEdit'" v-bind="model" @close="onClose" @save="onGroupSave" />
<OptionEdit v-else-if="model.action === 'optionEdit'" v-bind="model" @close="onClose" @save="onSave" />
<Variant v-else-if="model.action === 'variantEdit'" v-bind="model" @close="onClose" @save="onSave" />
<CreateMissing v-else-if="model.action === 'createMissing'" v-bind="model" @close="onClose" @save="onSave" />
<Translation v-else-if="model.action === 'translationEdit'" v-bind="model" @close="onClose" @save="onSave" />
</template>
</template>

View File

@ -0,0 +1,46 @@
<script setup>
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import MultiSelect from "primevue/multiselect";
import {onMounted, ref} from "vue";
import axios from "axios";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
const props = defineProps({
articleId: String,
});
const emit = defineEmits(['save', 'close']);
const model = ref({});
onMounted(async () => {
model.value = await axios.get('index.php?module=matrixprodukt&action=artikel&cmd=createMissing', {
params: {...props}
}).then(response => { return {...props, ...response.data}})
})
async function save() {
await axios.post('index.php?module=matrixprodukt&action=artikel&cmd=createMissing', {...props, ...model.value})
.catch(AlertErrorHandler)
.then(() => {emit('save')});
}
</script>
<template>
<Dialog visible modal header="Variante" style="width: 500px" @update:visible="emit('close')">
<div class="grid gap-1" style="grid-template-columns: 25% 75%;">
<template v-for="group in model.groups">
<label>{{ group.name }}</label>
<MultiSelect v-model="group.selected" :options="group.options" optionLabel="name" optionValue="value" />
</template>
</div>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="ERSTELLEN" @click="save" />
</template>
</Dialog>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,72 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import {ref, onMounted} from "vue";
import axios from "axios";
import Dialog from "primevue/dialog";
import Button from "primevue/button";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
import AutoComplete from "@res/vue/AutoComplete.vue";
const props = defineProps({
groupId: String,
articleId: String
});
const emit = defineEmits(['save', 'close']);
const model = ref({});
onMounted(async () => {
if (props.groupId > 0) {
const url = props.articleId > 0
? 'index.php?module=matrixprodukt&action=artikel&cmd=groupedit'
: 'index.php?module=matrixprodukt&action=list&cmd=edit';
model.value = await axios.get(url, {
params: props
}).then(response => response.data)
}
})
async function save() {
if (!parseInt(props.groupId) > 0)
model.value.groupId = 0;
const url = props.articleId > 0
? 'index.php?module=matrixprodukt&action=artikel&cmd=groupsave'
: 'index.php?module=matrixprodukt&action=list&cmd=save';
await axios.post(url, {...props, ...model.value})
.catch(AlertErrorHandler)
.then(() => {emit('save')});
}
</script>
<template>
<Dialog visible modal header="Gruppe anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')" class="p-fluid">
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_group_name">Name:</label>
<input type="text" v-model="model.name" required />
<label for="matrixProduct_group_nameExternal">Name Extern:</label>
<input type="text" v-model="model.nameExternal" />
<label for="matrixProduct_group_project">Projekt:</label>
<AutoComplete
v-model="model.project"
:optionLabel="item => [item.abkuerzung, item.name].join(' ')"
ajaxFilter="projektname"
forceSelection
/>
<label v-if="articleId" for="matrixProduct_group_sort">Sortierung:</label>
<input v-if="articleId" type="text" v-model="model.sort">
<label for="matrixProduct_group_required">Pflicht:</label>
<input type="checkbox" v-model="model.required" class="justify-self-start">
<label for="matrixProduct_group_active">Aktiv:</label>
<input type="checkbox" v-model="model.active" class="justify-self-start">
</div>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" :disabled="!model.name"/>
</template>
</Dialog>
</template>

View File

@ -0,0 +1,63 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import {ref, onMounted} from "vue";
import axios from "axios";
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
const props = defineProps({
optionId: String,
groupId: String,
articleId: String
});
const emit = defineEmits(['save', 'close']);
const model = ref({});
onMounted(async () => {
if (props.optionId > 0) {
const url = props.articleId > 0
? 'index.php?module=matrixprodukt&action=artikel&cmd=optionedit'
: 'index.php?module=matrixprodukt&action=optionenlist&cmd=edit';
model.value = await axios.get(url, {
params: props
}).then(response => response.data)
}
})
async function save() {
const url = props.articleId > 0
? 'index.php?module=matrixprodukt&action=artikel&cmd=optionsave'
: 'index.php?module=matrixprodukt&action=optionenlist&cmd=save';
await axios.post(url, {...props, ...model.value})
.then(() => {emit('save')})
.catch(AlertErrorHandler);
}
</script>
<template>
<Dialog visible modal header="Option anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')">
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_option_name">Name:</label>
<input id="matrixProduct_option_name" type="text" v-model="model.name" required />
<label for="matrixProduct_option_nameExternal">Name Extern:</label>
<input id="matrixProduct_option_nameExternal" type="text" v-model="model.nameExternal" />
<label for="matrixProduct_option_articleNumberSuffix">Artikelnummer-Suffix:</label>
<input id="matrixProduct_option_articleNumberSuffix" type="text" v-model="model.articleNumberSuffix" />
<label for="matrixProduct_option_sort">Sortierung:</label>
<input id="matrixProduct_option_sort" type="text" v-model="model.sort" />
<label for="matrixProduct_option_active">Aktiv:</label>
<input id="matrixProduct_option_active" type="checkbox" v-model="model.active" class="justify-self-start" />
</div>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" :disabled="!model.name" />
</template>
</Dialog>
</template>

View File

@ -0,0 +1,85 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import {ref, onMounted} from "vue";
import axios from "axios";
import Dialog from "primevue/dialog";
import Button from "primevue/button";
import Dropdown from "primevue/dropdown";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
const props = defineProps({
type: String,
id: String,
});
const emit = defineEmits(['save', 'close']);
const model = ref({});
const languages = ref([]);
onMounted(async () => {
if (props.id > 0) {
const url = 'index.php?module=matrixprodukt&action=translation&cmd=edit';
model.value = await axios.get(url, {
params: props
}).then(response => response.data)
}
axios.get('index.php',
{
params: {
module: 'ajax',
action: 'filter',
filtername: 'activelanguages',
object: true
}
}).then(response => {
languages.value = response.data;
});
})
async function save() {
if (!parseInt(props.id) > 0)
model.value.id = 0;
const url = 'index.php?module=matrixprodukt&action=translation&cmd=save';
await axios.post(url, {...props, ...model.value})
.catch(AlertErrorHandler)
.then(() => {emit('save')});
}
function ready() {
if (model.value.nameExternalFrom && !model.value.nameExternalTo)
return false;
return model.value.languageTo && model.value.nameFrom && model.value.nameTo;
}
</script>
<template>
<Dialog visible modal header="Übersetzung anlegen/bearbeiten" style="width: 500px" @update:visible="emit('close')" class="p-fluid">
<div class="grid gap-1" style="grid-template-columns: 25% 75%">
<label for="matrixProduct_nameFrom">DE Name:</label>
<input type="text" v-model="model.nameFrom" required />
<label for="matrixProduct_nameExternalFrom">DE Name Extern:</label>
<input type="text" v-model="model.nameExternalFrom" />
<label for="matrixProduct_languageTo">Sprache:</label>
<Dropdown
v-model="model.languageTo"
:options="languages"
option-label="bezeichnung_de"
option-value="iso"
/>
<label for="matrixProduct_nameTo">Übersetzung Name:</label>
<input type="text" v-model="model.nameTo" required>
<label for="matrixProduct_nameTo">Übersetzung Name Extern:</label>
<input type="text" v-model="model.nameExternalTo" :required="model.nameExternalFrom">
</div>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" :disabled="!ready()"/>
</template>
</Dialog>
</template>

View File

@ -0,0 +1,61 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<script setup>
import AutoComplete from "@res/vue/AutoComplete.vue";
import Button from "primevue/button";
import Dialog from "primevue/dialog";
import Dropdown from "primevue/dropdown";
import {onMounted, ref} from "vue";
import axios from "axios";
import {AlertErrorHandler} from "@res/js/ajaxErrorHandler";
const props = defineProps({
articleId: String,
variantId: String,
});
const emit = defineEmits(['save', 'close']);
const model = ref({});
onMounted(async () => {
model.value = await axios.get('index.php?module=matrixprodukt&action=artikel&cmd=variantedit', {
params: {...props}
}).then(response => { return {...props, ...response.data}})
})
async function save() {
await axios.post('index.php?module=matrixprodukt&action=artikel&cmd=variantsave', {...props, ...model.value})
.catch(AlertErrorHandler)
.then(() => {emit('save')});
}
const buttons = {
abbrechen: () => emit('close'),
speichern: save
}
</script>
<template>
<Dialog visible modal header="Variante" style="width: 500px" @update:visible="emit('close')">
<div class="grid gap-1" style="grid-template-columns: 25% 75%;">
<label>Artikel</label>
<AutoComplete v-model="model.variant"
:optionLabel="(item) => [item.nummer, item.name].join(' ')"
ajax-filter="artikelnummer"
forceSelection
/>
<template v-for="group in model.groups">
<label>{{ group.name }}</label>
<Dropdown v-model="group.selected" :options="group.options" optionLabel="name" optionValue="value" />
</template>
</div>
<template #footer>
<Button label="ABBRECHEN" @click="emit('close')" />
<Button label="SPEICHERN" @click="save" />
</template>
</Dialog>
</template>

View File

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2023 Andreas Palm
//
// SPDX-License-Identifier: LicenseRef-EGPL-3.1
import App from "./App.vue";
import {createVueApp} from "@res/js/vue";
const app = createVueApp(App).mount('#vueapp')

View File

@ -1,86 +0,0 @@
$(document).ready(function() {
$('a.groupheadline').on('click',function(){
if(parseInt($(this).data('id')) > 0) {
$('#GroupheadlineDialogGroupId').val($(this).data('id'));
$('#GroupheadlineDialogArticleId').val($(this).data('article'));
$('#GroupheadlineDialogGroupName').val($(this).data('name'));
$('#GroupheadlineDialog').dialog('open');
}
});
});
$('#GroupheadlineDialog').dialog(
{
modal: true,
autoOpen: false,
minWidth: 940,
title:'',
buttons: {
'ABBRECHEN': function() {
$(this).dialog('close');
},
'ÄNDERN': function()
{
if($('#GroupheadlineDialogGroupName').val()+'' !== '') {
$.ajax({
url: 'index.php?module=matrixprodukt&action=artikel&cmd=changegroupname',
type: 'POST',
dataType: 'json',
data: {
groupId: $('#GroupheadlineDialogGroupId').val(),
groupName: $('#GroupheadlineDialogGroupName').val()
},
success: function (data) {
if(typeof data.status != 'undefined' && data.status == 1) {
window.location.href = window.location.href.split('#')[0];
}
},
beforeSend: function () {
}
});
}else{
alert('Bitte eine Bezeichnung angeben');
}
},
'LÖSCHEN': function() {
$.ajax({
url: 'index.php?module=matrixprodukt&action=artikel&cmd=deletegroup',
type: 'POST',
dataType: 'json',
data: {
groupId: $('#GroupheadlineDialogGroupId').val()
},
success: function(data) {
if(typeof data.status != 'undefined' && data.status == 1) {
if (typeof data.confirm != 'undefined' && data.confirm == 1) {
if(confirm('Die Gruppe enthält Option, sollen die Gruppe mit diesen Optionen wirklich gelöscht werden?'))
{
$.ajax({
url: 'index.php?module=matrixprodukt&action=artikel&cmd=deletegroup',
type: 'POST',
dataType: 'json',
data: {
force:1,groupId: $('#GroupheadlineDialogGroupId').val()
},success: function(data) {
window.location.href = window.location.href.split('#')[0];
}
});
}
} else {
window.location.href = window.location.href.split('#')[0];
}
}
},
beforeSend: function() {
}
});
}
},
close: function(event, ui){
}
});

View File

@ -1,513 +0,0 @@
var MatrixproductListview = function ($) {
"use strict";
var me = {
storage:{
selectedArticles: '',
optionTableloaded: false,
groupTableloaded: false,
articleTableloaded: false,
},
selector: {
divlistview: 'div.listview',
popupGroup: '#popupgroup',
popupOption: '#popupoption',
popupArticleCreate: '#popuparcticlecreate',
popupArticle: '#popuparticle',
btnNewGroup: '#newGroup',
btnNewOption: '#newOption',
articleTable: '#matrixprodukt_list_view',
groupTable: '#matrixprodukt_list_view_group',
optionTable: '#matrixprodukt_list_view_options',
},
editArticle: function(id) {
if(id > 0) {
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listgetarticle',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
listid: id
},
success: function (data) {
$('div#options').html(data.optionhtml);
$('#articlelistid').val(data.id);
$(me.selector.popupArticle).dialog('open');
}
});
}
},
editGroup: function(id){
$('#groupid').val(id);
if(id > 0) {
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listgetgroup',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
groupid: $('#groupid').val()
},
success: function (data) {
$('#groupname').val(data.name);
$(me.selector.popupGroup).dialog('open');
}
});
return;
}
$(me.selector.popupGroup).dialog('open');
},
deleteGroup: function(id) {
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listdeletegroupcheck',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
groupid: id
},
success: function (data) {
if(typeof data.message) {
if(!confirm(data.message)) {
return;
}
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listdeletegroup',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
groupid: data.groupid
},
success: function (data) {
if(typeof data.url !== 'undefined') {
window.location.href=data.url;
return;
}
$(me.selector.optionTable).DataTable().ajax.reload();
$(me.selector.groupTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
},
error: function()
{
$(me.selector.optionTable).DataTable().ajax.reload();
$(me.selector.groupTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
}
});
}
}
});
},
deleteOption: function(id) {
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listdeleteoptioncheck',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
optionid: id
},
success: function (data) {
if(typeof data.message) {
if(!confirm(data.message)) {
return;
}
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listdeleteoption',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
optionid: data.optionid
},
success: function () {
$(me.selector.optionTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
},
error: function() {
$(me.selector.optionTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
}
});
}
}
});
},
generateList: function()
{
$('#tabs-1').loadingOverlay('show');
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=generatelist',
data: {
articleid: $(me.selector.popupGroup).data('articleid')
},
success: function (data) {
if(typeof data.url != 'undefined') {
window.location.href=data.url;
return;
}
$('#tabs-1').loadingOverlay('remove');
},
error:function()
{
$('#tabs-1').loadingOverlay('remove');
}
});
},
massedit: function(){
me.storage.selectedArticles= '';
$(me.selector.articleTable).find('input:checked').each(function(){
if($(this).data('articleid') > 0) {
if (me.storage.selectedArticles !== '') {
me.storage.selectedArticles
= me.storage.selectedArticles + ';';
}
me.storage.selectedArticles
= me.storage.selectedArticles
+ $(this).data('articleid')
}
});
if(me.storage.selectedArticles !== '') {
matrixproduktedit_open(me.storage.selectedArticles);
}
else {
alert('Kein Artikel ausgwählt');
}
},
createallmissingarticles: function() {
me.storage.selectedArticles= 'ALL';
$('#listids').val(me.storage.selectedArticles);
$(me.selector.popupArticleCreate).dialog('open');
},
createMissingArticles: function(){
me.storage.selectedArticles= '';
$(me.selector.articleTable).find('input:checked').each(function(){
if(me.storage.selectedArticles !== '') {
me.storage.selectedArticles
=me.storage.selectedArticles + ';';
}
me.storage.selectedArticles
=me.storage.selectedArticles
+$(this).data('id')
});
if(me.storage.selectedArticles !== '') {
$('#listids').val(me.storage.selectedArticles);
$(me.selector.popupArticleCreate).dialog('open');
}
},
editOption: function(id){
$('#optionid').val(id);
if(id > 0) {
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listgetoption',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
optionid: $('#optionid').val()
},
success: function (data) {
$('#optionname').val(data.name);
$('#optiongroup').html(data.groups);
$('#optiongroup').val(data.gruppe);
$(me.selector.popupOption).dialog('open');
}
});
return;
}
else {
$('#optionname').val('');
}
$(me.selector.popupOption).dialog('open');
},
articeTableAfterReload:function(){
$(me.selector.articleTable).find('img.editarticle').on('click',function(){
me.editArticle($(this).data('id'));
});
me.storage.articleTableloaded = true;
},
groupTableAfterReload:function(){
$(me.selector.groupTable).find('img.editgroup').on('click',function(){
me.editGroup($(this).data('id'));
});
$(me.selector.groupTable).find('img.deletegroup').on('click',function(){
me.deleteGroup($(this).data('id'));
});
me.storage.groupTableloaded = true;
},
optionTableAfterReload:function(){
$(me.selector.optionTable).find('img.editoption').on('click',function(){
me.editOption($(this).data('id'));
});
$(me.selector.optionTable).find('img.deleteoption').on('click',function(){
me.deleteOption($(this).data('id'));
});
me.storage.optionTableloaded = true;
},
createMissingAriclesSave: function()
{
$(me.selector.popupArticleCreate).parent().loadingOverlay('show');
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=createarticles',
data: {
listids:$('#listids').val(),
articleid: $(me.selector.popupGroup).data('articleid'),
fromcategory: $('#fromcategory').prop('checked')?1:0,
fromoption: $('#fromoption').prop('checked')?1:0,
fromsuffix: $('#fromsuffix').prop('checked')?1:0,
fromprefix: $('#fromprefix').prop('checked')?1:0,
prefixseparator: $('#prefixseparator').val(),
prefixcount: $('#prefixcount').val(),
prefixnextnumber: $('#prefixnextnumber').val(),
appendname: $('#prefixseparator').prop('checked')?1:0,
nextprefixnumber: $('#nextprefixnumber').val(),
},
success: function (data) {
$(me.selector.popupArticleCreate).parent().loadingOverlay('remove');
if(typeof data.continue != 'undefined' && data.continue == 1) {
if(typeof data.nextprefixnumber != 'undefined') {
$('#nextprefixnumber').val(data.nextprefixnumber);
}
me.createMissingAriclesSave();
return;
}
$('#nextprefixnumber').val('');
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupArticleCreate).dialog('close');
},
error: function()
{
$(me.selector.popupArticleCreate).parent().loadingOverlay('remove');
$('#nextprefixnumber').val('');
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupArticleCreate).dialog('close');
}
});
},
initListView: function(){
$(me.selector.popupGroup).dialog(
{
modal: true,
autoOpen: false,
minWidth: 940,
title: '',
buttons: {
'ABBRECHEN':function(){
$(me.selector.popupGroup).dialog('close');
},
'SPEICHERN': function () {
$(me.selector.popupGroup).parent().loadingOverlay('show');
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listsavegroup',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
name: $('#groupname').val(),
groupid: $('#groupid').val()
},
success: function (data) {
if(typeof data.url != 'undefined') {
window.location.href=data.url;
return;
}
$(me.selector.groupTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupGroup).parent().loadingOverlay('remove');
},
error: function()
{
$(me.selector.groupTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupGroup).parent().loadingOverlay('remove');
}
});
},
}
}
);
$(me.selector.popupArticleCreate).dialog(
{
modal: true,
autoOpen: false,
minWidth: 940,
title: '',
buttons: {
'ABBRECHEN':function(){
$(me.selector.popupArticleCreate).dialog('close');
},
'SPEICHERN': function () {
me.createMissingAriclesSave();
},
}
}
);
$(me.selector.popupArticle).dialog(
{
modal: true,
autoOpen: false,
minWidth: 940,
title: '',
buttons: {
'ABBRECHEN':function(){
$(me.selector.popupArticle).dialog('close');
},
'SPEICHERN': function () {
$(me.selector.popupArticle).parent().loadingOverlay('show');
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listsavearticle',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
listid:$('#articlelistid').val(),
option1: (typeof $('#option1').val() != 'undefined'?$('#option1').val():0),
option2: (typeof $('#option2').val() != 'undefined'?$('#option2').val():0),
option3: (typeof $('#option3').val() != 'undefined'?$('#option3').val():0),
option4: (typeof $('#option4').val() != 'undefined'?$('#option4').val():0),
option5: (typeof $('#option5').val() != 'undefined'?$('#option5').val():0),
option6: (typeof $('#option6').val() != 'undefined'?$('#option6').val():0),
option7: (typeof $('#option7').val() != 'undefined'?$('#option7').val():0),
option8: (typeof $('#option8').val() != 'undefined'?$('#option8').val():0),
option9: (typeof $('#option9').val() != 'undefined'?$('#option9').val():0),
option10: (typeof $('#option10').val() != 'undefined'?$('#option10').val():0),
option11: (typeof $('#option11').val() != 'undefined'?$('#option11').val():0),
option12: (typeof $('#option12').val() != 'undefined'?$('#option12').val():0),
option13: (typeof $('#option13').val() != 'undefined'?$('#option13').val():0),
option14: (typeof $('#option14').val() != 'undefined'?$('#option14').val():0),
option15: (typeof $('#option15').val() != 'undefined'?$('#option15').val():0),
option16: (typeof $('#option16').val() != 'undefined'?$('#option16').val():0),
option17: (typeof $('#option17').val() != 'undefined'?$('#option17').val():0),
option18: (typeof $('#option18').val() != 'undefined'?$('#option18').val():0),
option19: (typeof $('#option19').val() != 'undefined'?$('#option19').val():0),
option20: (typeof $('#option20').val() != 'undefined'?$('#option20').val():0),
},
success: function () {
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupArticle).parent().loadingOverlay('remove');
$(me.selector.popupArticle).dialog('close');
},
error: function() {
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupArticle).parent().loadingOverlay('remove');
$(me.selector.popupArticle).dialog('close');
}
});
},
}
}
);
$(me.selector.popupOption).dialog(
{
modal: true,
autoOpen: false,
minWidth: 940,
title: '',
buttons: {
'ABBRECHEN':function(){
$(me.selector.popupOption).dialog('close');
},
'SPEICHERN': function () {
$(me.selector.popupOption).parent().loadingOverlay('show');
$.ajax({
type: 'POST',
dataType: 'json',
url: 'index.php?module=matrixprodukt&action=artikel&cmd=listsaveoption',
data: {
articleid: $(me.selector.popupGroup).data('articleid'),
name: $('#optionname').val(),
groupid: $('#optiongroup').val(),
optionid: $('#optionid').val()
},
success: function () {
$(me.selector.optionTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupOption).parent().loadingOverlay('remove');
$(me.selector.popupOption).dialog('close');
},
error: function(){
$(me.selector.optionTable).DataTable().ajax.reload();
$(me.selector.articleTable).DataTable().ajax.reload();
$(me.selector.popupOption).parent().loadingOverlay('remove');
$(me.selector.popupOption).dialog('close');
}
});
},
}
}
);
$(me.selector.popupGroup).toggleClass('hidden', false);
$(me.selector.popupOption).toggleClass('hidden', false);
$(me.selector.popupArticle).toggleClass('hidden', false);
$(me.selector.popupArticleCreate).toggleClass('hidden', false);
$(me.selector.articleTable).on('afterreload', function(){
me.articeTableAfterReload();
});
$(me.selector.optionTable).on('afterreload', function(){
me.optionTableAfterReload();
});
$(me.selector.groupTable).on('afterreload', function(){
me.groupTableAfterReload();
});
$(me.selector.btnNewGroup).on('click',function(){
me.editGroup(0);
});
$(me.selector.btnNewOption).on('click',function(){
me.editOption(0);
});
$('#changeall').on('change',function(){
$(me.selector.articleTable).find('input.select').prop('checked', $('#changeall').prop('checked'));
});
$('#createmissingarticles').on('click',function () {
me.createMissingArticles();
});
$('#createallmissingarticles').on('click',function () {
me.createallmissingarticles();
});
$('#massedit').on('click',function () {
me.massedit();
});
$('#generatelist').on('click',function () {
me.generateList();
});
if(!me.storage.optionTableloaded) {
$(me.selector.optionTable).DataTable().ajax.reload();
}
if(!me.storage.groupTableloaded) {
$(me.selector.groupTable).DataTable().ajax.reload();
}
if(!me.storage.articleTableloaded) {
$(me.selector.articleTable).DataTable().ajax.reload();
}
},
init: function () {
if($(me.selector.divlistview).length) {
me.initListView();
}
}
};
return {
init: me.init,
}
}(jQuery);
$(document).ready(function () {
MatrixproductListview.init();
});

View File

@ -0,0 +1,15 @@
<?php
// SPDX-FileCopyrightText: 2024 Andreas Palm
//
// SPDX-License-Identifier: AGPL-3.0-only
namespace Xentral\Modules\Onlineshop\Data;
enum OrderStatus
{
case Imported;
case InProgress;
case Completed;
case Cancelled;
}

View File

@ -0,0 +1,48 @@
<?php
// SPDX-FileCopyrightText: 2024 Andreas Palm
//
// SPDX-License-Identifier: AGPL-3.0-only
namespace Xentral\Modules\Onlineshop\Data;
class OrderStatusUpdateRequest
{
/**
* @var int ID of the (primary/imported) order (ERP domain)
*/
public int $orderId;
/**
* @var string ID of the order (Shop domain)
*/
public string $shopOrderId;
/**
* @var OrderStatus current order status
*/
public OrderStatus $orderStatus;
/**
* @var Shipment[] list of shipments for this order
*/
public array $shipments;
public function getTrackingNumberList() : array {
$list = [];
foreach ($this->shipments as $shipment) {
if (!empty($shipment->trackingNumber))
$list[] = $shipment->trackingNumber;
}
return $list;
}
public function getTrackingUrlList() : array {
$list = [];
foreach ($this->shipments as $shipment) {
if (!empty($shipment->trackingUrl))
$list[] = $shipment->trackingUrl;
}
return $list;
}
}

View File

@ -0,0 +1,30 @@
<?php
// SPDX-FileCopyrightText: 2024 Andreas Palm
//
// SPDX-License-Identifier: AGPL-3.0-only
namespace Xentral\Modules\Onlineshop\Data;
class Shipment
{
/**
* @var int ID of the shipment (ERP domain)
*/
public int $id;
/**
* @var string plain tracking number
*/
public string $trackingNumber;
/**
* @var string URL to view tracking details
*/
public string $trackingUrl;
/**
* @var string shipping method (after mapping to Shop domain)
*/
public string $shippingMethod;
}

View File

@ -387,15 +387,16 @@ function GetLaender()
$join = '';
$where = '';
$app->erp->RunHook('shop_rueckmeldung', 2, $join, $where);
$sql = "SELECT a.id,apro.zeit, a.shop, l.id as lieferschein, v.id as versandid, l.projekt
FROM auftrag AS a
LEFT JOIN lieferschein AS l on l.auftragid = a.id
LEFT JOIN auftrag_protokoll AS apro ON a.id = apro.auftrag AND apro.grund LIKE 'Auftrag importiert vom Shop'
LEFT JOIN projekt AS pr ON l.projekt = pr.id
LEFT JOIN versand AS v ON v.lieferschein = l.id
$sql = "SELECT DISTINCT a.id, a.shop
FROM auftrag a
LEFT JOIN lieferschein l on l.auftragid = a.id
LEFT JOIN projekt pr ON l.projekt = pr.id
LEFT JOIN lieferschein_position lp ON lp.lieferschein = l.id
LEFT JOIN versandpaket_lieferschein_position vlp ON vlp.lieferschein_position = lp.id
LEFT JOIN versandpakete v ON (v.lieferschein_ohne_pos = l.id OR v.id = vlp.versandpaket)
$join
WHERE a.status = 'abgeschlossen' AND $subwhere AND
DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 2 WEEK),'%Y-%m-%d') < a.datum AND
DATE_SUB(NOW(),INTERVAL 2 WEEK) < a.datum AND
a.shopextstatus <> 'abgeschlossen' AND a.shop > 0 AND
(
( v.tracking <> '' AND l.status = 'versendet') OR

View File

@ -945,7 +945,9 @@ if($shops) {
{
$onlinebestellnummer = $tmpwarenkorb['auftrag'];
}
$projekt = $app->DB->Select("SELECT projekt FROM shopexport WHERE id = '$id' LIMIT 1");
if(!empty($tmpwarenkorb['projekt']) && $app->DB->Select("SELECT id FROM projekt WHERE id = '".(int)$tmpwarenkorb['projekt']."' LIMIT 1"))$projekt = (int)$tmpwarenkorb['projekt'];
if(isset($tmpwarenkorb['subshop']) && $tmpwarenkorb['subshop'])
{

27296
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,15 +11,19 @@
"production": "./node_modules/cross-env/src/bin/cross-env-shell.js NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"raml2html": "^7.6.0",
"raml2html-werk-theme": "^1.1.0",
"cross-env": "^7.0",
"laravel-mix": "^5.0.1",
"raml2html": "^7.6.0",
"raml2html-werk-theme": "^1.1.0",
"resolve-url-loader": "^3.1.0",
"sass": "^1.15.2",
"sass-loader": "^8.0.0"
},
"dependencies": {
"vue": "^2.6.12"
"@vitejs/plugin-vue": "^4.3.4",
"axios": "^1.5.0",
"primevue": "^3.35.0",
"vite": "^4.4.9",
"vue": "^3.3.4"
}
}

View File

@ -1,4 +1,12 @@
<?php
/*
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*
@ -317,9 +325,14 @@ class Player {
$moduleClassName = strtoupper($module[0]) . substr($module, 1);
$this->app->ModuleScriptCache->IncludeModule($moduleClassName);
$this->app->ModuleScriptCache->IncludeJavascriptModules('_theme_', ['www/themes/new/js/main.js']);
$this->app->Tpl->Add('MODULESTYLESHEET', $this->app->ModuleScriptCache->GetStylesheetHtmlTags());
$this->app->Tpl->Add('MODULEJAVASCRIPTHEAD', $this->app->ModuleScriptCache->GetJavascriptHtmlTags('head'));
$this->app->Tpl->Add('MODULEJAVASCRIPTBODY', $this->app->ModuleScriptCache->GetJavascriptHtmlTags('body'));
$this->app->Tpl->Set('JAVASCRIPTMODULES', $this->app->ModuleScriptCache->GetJavascriptModulesHtmlTags());
if (defined('VITE_DEV_SERVER')) {
$this->app->Tpl->Add('ADDITIONALCSPHEADER', VITE_DEV_SERVER.' ');
}
$permission = true;
if(isset($myApp) && method_exists($myApp,'CheckRights'))$permission = $myApp->CheckRights();
@ -412,7 +425,7 @@ class Player {
else{
$this->app->Tpl->Set('VUEJS', 'vue.min.js');
$this->app->erp->RunHook('before_final_parse_page');
$this->app->erp->RunHook('before_final_parse_page');
echo $this->app->Tpl->FinalParse('page.tpl');
}
}

View File

@ -1,4 +1,12 @@
<?php
<?php
/*
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*
@ -10,8 +18,8 @@
* to obtain the text of the corresponding license version.
*
**** END OF COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*/
?>
*/
?>
<?php
/**
@ -34,12 +42,19 @@ class ModuleScriptCache
/** @var string $relativeCacheDir Relativer Pfad zum Cache-Ordner (ausgehend von www) */
protected $relativeCacheDir;
protected $assetDir;
/** @var object $assetManifest Parsed manifest.json from vite */
protected $assetManifest;
/** @var array $javascriptFiles Absolute Pfade zu Javascript-Dateien die gecached werden sollen */
protected $javascriptFiles = [
'head' => [],
'body' => [],
];
protected $javascriptModules = [];
/** @var array $stylesheetFiles Absolute Pfade zu Stylesheet-Dateien die gecached werden sollen */
protected $stylesheetFiles = [];
@ -48,6 +63,8 @@ class ModuleScriptCache
$this->baseDir = dirname(dirname(__DIR__));
$this->absoluteCacheDir = $this->baseDir . '/www/cache';
$this->relativeCacheDir = './cache';
$this->assetDir = '/dist';
$this->assetManifest = json_decode(file_get_contents($this->baseDir. '/www' . $this->assetDir . '/manifest.json'));
// Cache-Ordner anzulegen, falls nicht existent
if (!is_dir($this->absoluteCacheDir)) {
@ -74,6 +91,7 @@ class ModuleScriptCache
// Javascript- und Stylesheet-Dateien sind als Eigenschaft im Modul definiert
$javascript = $this->GetClassProperty($legacyModuleClassName, 'javascript');
$stylesheet = $this->GetClassProperty($legacyModuleClassName, 'stylesheet');
$jsmodules = $this->GetClassProperty($legacyModuleClassName, 'jsmodules');
// Falls nicht im Modul definiert > Defaults verwenden
if (empty($javascript)) {
@ -82,9 +100,13 @@ class ModuleScriptCache
if (empty($stylesheet)) {
$stylesheet = [$this->GetDefaultModuleStylesheetFile($newModuleName)];
}
if (empty($jsmodules)) {
$jsmodules = $this->GetDefaultModuleJavascriptModules($newModuleName);
}
$this->IncludeJavascriptFiles($newModuleName, $javascript);
$this->IncludeStylesheetFiles($newModuleName, $stylesheet);
$this->IncludeJavascriptModules($newModuleName, $jsmodules);
}
/**
@ -193,6 +215,20 @@ class ModuleScriptCache
}
}
public function IncludeJavascriptModules(string $moduleName, array $files) : void
{
foreach ($files as $file) {
$realPath = realpath($this->baseDir . '/' . $file);
if (!is_file($realPath))
continue;
if (isset($this->assetManifest->$file))
$this->javascriptModules[] = $this->assetManifest->$file;
else
$this->javascriptModules[] = $realPath;
}
}
/**
* @param string $cacheName Name unter dem die Cache-Datei zusammengefasst werden
* @param array $files Array mit relativen Pfaden zur Xentral-Installation
@ -258,6 +294,36 @@ class ModuleScriptCache
return $html;
}
public function GetJavascriptModulesHtmlTags() : string
{
if (empty($this->javascriptModules))
return '';
$html = '';
foreach ($this->javascriptModules as $module) {
if (is_object($module)) {
if (defined('VITE_DEV_SERVER')) {
$url = 'http://' . VITE_DEV_SERVER . '/' . $module->src;
} else {
$url = '.'.$this->assetDir . '/' . $module->file;
if (isset($module->css)) {
foreach ($module->css as $css)
$html .= sprintf('<link rel="stylesheet" type="text/css" href="%s" />', '.'.$this->assetDir.'/'.$css);
$html .= "\r\n";
}
}
} elseif (str_starts_with($module,$this->baseDir.'/www')) {
$url = '.'.substr($module, strlen($this->baseDir)+4);
}
if (isset($url)) {
$html .= sprintf('<script type="module" src="%s"></script>', $url);
$html .= "\r\n";
}
}
return $html;
}
/**
* @return string
*/
@ -389,6 +455,18 @@ class ModuleScriptCache
return sprintf('./classes/Modules/%s/www/js/%s.js', $moduleName, strtolower($moduleName));
}
/**
* @param string $moduleName
* @return string relative path to default Javascript-Module-File
*/
protected function GetDefaultModuleJavascriptModules(string $moduleName): array
{
return [
sprintf('classes/Modules/%s/www/js/entry.js', $moduleName),
sprintf('classes/Modules/%s/www/js/entry.jsx', $moduleName)
];
}
/**
* @param string $moduleName
*

View File

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@import "autocomplete.css";
@import "checkbox.css";
@import "dialog.css";
@import "dropdown.css";
@import "listbox.css";
@import "multiselect.css";
.p-component-overlay {
background-color: rgba(170,170,170,0.7);
}
.p-button {
background-color: var(--button-primary-background);
padding: 6px;
margin: 3px;
color: white;
border-radius: 3px;
border: 1px solid var(--button-primary-border-color);
font-size: 14px;
}
.p-icon {
width: 1em;
height: 1em;
}

View File

@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.p-autocomplete .p-autocomplete-loader {
right: 0.75rem;
}
.p-autocomplete.p-autocomplete-dd .p-autocomplete-loader {
right: 3.107rem;
}
.p-autocomplete:not(.p-disabled):hover .p-autocomplete-multiple-container {
border-color: #ced4da;
}
.p-autocomplete:not(.p-disabled).p-focus .p-autocomplete-multiple-container {
outline: 0 none;
outline-offset: 0;
box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
border-color: #007bff;
}
.p-autocomplete .p-autocomplete-multiple-container {
padding: 0.25rem 0.75rem;
gap: 0.5rem;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-input-token {
padding: 0.25rem 0;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-input-token input {
font-family: inherit;
font-feature-settings: inherit;
font-size: inherit;
color: #212529;
padding: 0;
margin: 0;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token {
padding: 0.25rem 0.75rem;
background: #dee2e6;
color: #212529;
border-radius: 16px;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token .p-autocomplete-token-icon {
margin-left: 0.5rem;
}
.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token.p-focus {
background: #ced4da;
color: #212529;
}
.p-autocomplete.p-invalid.p-component > .p-inputtext {
border-color: #dc3545;
}
.p-autocomplete-panel {
background: #ffffff;
color: var(--grey);
border: 1px solid var(--fieldset-dark);
border-radius: 4px;
box-shadow: none;
}
.p-autocomplete-panel .p-autocomplete-items {
padding: 0.5rem 0;
}
.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item {
margin: 0;
padding: 3px;
border: 0 none;
color: #212529;
background: transparent;
transition: box-shadow 0.15s;
border-radius: 0;
}
.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item.p-highlight {
color: #ffffff;
background: #007bff;
}
.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item.p-highlight.p-focus {
background: #0067d6;
}
.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item:not(.p-highlight):not(.p-disabled).p-focus {
color: #212529;
background: #dee2e6;
}
.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item:not(.p-highlight):not(.p-disabled):hover {
color: #212529;
background: #e9ecef;
}
.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item-group {
margin: 0;
padding: 0.75rem 1rem;
color: #212529;
background: #ffffff;
font-weight: 600;
}
input[type=text].p-autocomplete-dd-input {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
.p-autocomplete-dd .p-autocomplete-dropdown {
border-top-left-radius: 0;
border-bottom-left-radius: 0px;
margin: 0;
padding: 3px;
}
.p-autocomplete-dd .p-autocomplete-dropdown svg {
width: 14px;
height: 14px;
}

View File

@ -0,0 +1,78 @@
@layer primevue {
.p-checkbox {
position: relative;
display: inline-flex;
user-select: none;
vertical-align: bottom;
}
.p-checkbox-input {
cursor: pointer;
}
.p-checkbox-box {
display: flex;
justify-content: center;
align-items: center;
}
}
.p-checkbox {
width: 20px;
height: 20px;
}
.p-checkbox .p-checkbox-input {
appearance: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
opacity: 0;
z-index: 1;
outline: 0 none;
border: 2px solid #ced4da;
border-radius: 4px;
}
.p-checkbox .p-checkbox-box {
border: 2px solid #ced4da;
background: #ffffff;
width: 20px;
height: 20px;
color: #212529;
border-radius: 4px;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
outline-color: transparent;
}
.p-checkbox .p-checkbox-box .p-checkbox-icon {
transition-duration: 0.15s;
color: #ffffff;
font-size: 14px;
}
.p-checkbox .p-checkbox-box .p-checkbox-icon.p-icon {
width: 14px;
height: 14px;
}
.p-checkbox .p-checkbox-box.p-highlight {
border-color: var(--button-primary-background);
background: var(--button-primary-background);
}
.p-checkbox:not(.p-disabled):has(.p-checkbox-input:hover) .p-checkbox-box {
border-color: #ced4da;
}
.p-checkbox:not(.p-disabled):has(.p-checkbox-input:hover) .p-checkbox-box.p-highlight {
border-color: #0062cc;
background: #0062cc;
color: #ffffff;
}
.p-checkbox:not(.p-disabled):has(.p-checkbox-input:focus-visible) .p-checkbox-box {
outline: 0 none;
outline-offset: 0;
box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
border-color: #007bff;
}
.p-checkbox.p-invalid > .p-checkbox-box {
border-color: #dc3545;
}

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.p-dialog {
background-color: var(--body-background);
opacity: 1;
}
.p-dialog-header {
color: #6d6d6f;
font-size: 14px;
font-weight: bold;
padding: .4em 1em;
}
.p-dialog .p-dialog-header .p-dialog-header-icon {
width: 2rem;
height: 2rem;
}
.p-dialog .p-dialog-content {
padding: 0.5em 1em;
}
.p-dialog .p-dialog-footer {
border-top: 1px solid var(--fieldset-dark);
padding: 0.3em 1em 0.5em 0.4em;
text-align: right;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}

View File

@ -0,0 +1,129 @@
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.p-dropdown {
background: #ffffff;
border: 1px solid #ced4da;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
border-radius: 4px;
padding: 4px 5px;
font-size: 11px;
}
.p-dropdown:not(.p-disabled):hover {
border-color: #ced4da;
}
.p-dropdown:not(.p-disabled).p-focus {
outline: 0 none;
outline-offset: 0;
box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
border-color: #007bff;
}
.p-dropdown.p-dropdown-clearable .p-dropdown-label {
padding-right: 1.75rem;
}
.p-dropdown .p-dropdown-label {
background: transparent;
border: 0 none;
color: #6d6d6f;
}
.p-dropdown .p-dropdown-label.p-placeholder {
color: #6c757d;
}
.p-dropdown .p-dropdown-label:focus, .p-dropdown .p-dropdown-label:enabled:focus {
outline: 0 none;
box-shadow: none;
}
.p-dropdown .p-dropdown-trigger {
background: transparent;
color: #495057;
width: 2.357rem;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.p-dropdown .p-dropdown-clear-icon {
color: #495057;
right: 2.357rem;
}
.p-dropdown.p-invalid.p-component {
border-color: #dc3545;
}
.p-dropdown-panel {
background: #ffffff;
color: var(--grey);
border: 1px solid var(--fieldset-dark);
border-radius: 4px;
box-shadow: none;
}
.p-dropdown-panel .p-dropdown-header {
padding: 0.75rem 1.5rem;
border-bottom: 1px solid #dee2e6;
color: #212529;
background: #efefef;
margin: 0;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
.p-dropdown-panel .p-dropdown-header .p-dropdown-filter {
padding-right: 1.75rem;
margin-right: -1.75rem;
}
.p-dropdown-panel .p-dropdown-header .p-dropdown-filter-icon {
right: 0.75rem;
color: #495057;
}
.p-dropdown-panel .p-dropdown-items {
padding: 0.5rem 0;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-item {
margin: 0;
padding: 3px;
border: 0 none;
color: #212529;
background: transparent;
transition: box-shadow 0.15s;
border-radius: 0;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-item.p-highlight {
color: #ffffff;
background: #007bff;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-item.p-highlight.p-focus {
background: #0067d6;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled).p-focus {
color: #212529;
background: #dee2e6;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled):hover {
color: #212529;
background: #e9ecef;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-item-group {
margin: 0;
padding: 0.75rem 1rem;
color: #212529;
background: #ffffff;
font-weight: 600;
}
.p-dropdown-panel .p-dropdown-items .p-dropdown-empty-message {
padding: 0.5rem 1.5rem;
color: #212529;
background: transparent;
}
.p-input-filled .p-dropdown {
background: #efefef;
}
.p-input-filled .p-dropdown:not(.p-disabled):hover {
background-color: #efefef;
}
.p-input-filled .p-dropdown:not(.p-disabled).p-focus {
background-color: #efefef;
}
.p-input-filled .p-dropdown:not(.p-disabled).p-focus .p-inputtext {
background-color: transparent;
}

View File

@ -0,0 +1,30 @@
.p-icon {
display: inline-block;
}
.p-icon-spin {
-webkit-animation: p-icon-spin 2s infinite linear;
animation: p-icon-spin 2s infinite linear;
}
@-webkit-keyframes p-icon-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes p-icon-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}

View File

@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
.p-listbox {
background: #ffffff;
color: #6d6d6f;
border: 1px solid #ced4da;
border-radius: 4px;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.p-listbox .p-listbox-header {
padding: 0.75rem 1.5rem;
border-bottom: 1px solid #dee2e6;
color: #212529;
background: #efefef;
margin: 0;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
.p-listbox .p-listbox-header .p-listbox-filter {
padding-right: 1.75rem;
}
.p-listbox .p-listbox-header .p-listbox-filter-icon {
right: 0.75rem;
color: #495057;
}
.p-listbox .p-listbox-list {
padding: 0.5rem 0;
outline: 0 none;
}
.p-listbox .p-listbox-list .p-listbox-item {
margin: 0;
padding: 0.5rem 1.5rem;
border: 0 none;
color: #212529;
transition: box-shadow 0.15s;
border-radius: 0;
}
.p-listbox .p-listbox-list .p-listbox-item.p-highlight {
color: #ffffff;
background: var(--green);
}
.p-listbox .p-listbox-list .p-listbox-item-group {
margin: 0;
padding: 0.75rem 1rem;
color: #212529;
background: #ffffff;
font-weight: 600;
}
.p-listbox .p-listbox-list .p-listbox-empty-message {
padding: 0.5rem 1.5rem;
color: #212529;
background: transparent;
}
.p-listbox:not(.p-disabled) .p-listbox-item.p-highlight.p-focus {
background: var(--green);
}
.p-listbox:not(.p-disabled) .p-listbox-item:not(.p-highlight):not(.p-disabled).p-focus {
color: #212529;
background: #dee2e6;
}
.p-listbox:not(.p-disabled) .p-listbox-item:not(.p-highlight):not(.p-disabled):hover {
color: #212529;
background: #e9ecef;
}
.p-listbox.p-focus {
outline: 0 none;
outline-offset: 0;
}
.p-listbox.p-invalid {
border-color: #dc3545;
}

View File

@ -0,0 +1,247 @@
@layer primevue {
.p-multiselect {
display: inline-flex;
cursor: pointer;
user-select: none;
}
.p-multiselect-trigger {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.p-multiselect-label-container {
overflow: hidden;
flex: 1 1 auto;
cursor: pointer;
}
.p-multiselect-label {
display: block;
white-space: nowrap;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
}
.p-multiselect-label-empty {
overflow: hidden;
visibility: hidden;
}
.p-multiselect-token {
cursor: default;
display: inline-flex;
align-items: center;
flex: 0 0 auto;
}
.p-multiselect-token-icon {
cursor: pointer;
}
.p-multiselect .p-multiselect-panel {
min-width: 100%;
}
.p-multiselect-items-wrapper {
overflow: auto;
}
.p-multiselect-items {
margin: 0;
padding: 0;
list-style-type: none;
}
.p-multiselect-item {
cursor: pointer;
display: flex;
align-items: center;
font-weight: normal;
white-space: nowrap;
position: relative;
overflow: hidden;
}
.p-multiselect-item-group {
cursor: auto;
}
.p-multiselect-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.p-multiselect-filter-container {
position: relative;
flex: 1 1 auto;
}
.p-multiselect-filter-icon {
position: absolute;
top: 50%;
margin-top: -0.5rem;
}
.p-multiselect-filter-container .p-inputtext {
width: 100%;
}
.p-multiselect-close {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
overflow: hidden;
position: relative;
margin-left: auto;
}
.p-fluid .p-multiselect {
display: flex;
}
}
.p-multiselect {
background: #ffffff;
border: 1px solid #ced4da;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
border-radius: 4px;
outline-color: transparent;
}
.p-multiselect:not(.p-disabled):hover {
border-color: #ced4da;
}
.p-multiselect:not(.p-disabled).p-focus {
outline: 0 none;
outline-offset: 0;
box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
border-color: #007bff;
}
.p-multiselect .p-multiselect-label {
padding: 0.25rem 0.75rem;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.p-multiselect .p-multiselect-label.p-placeholder {
color: #6c757d;
}
.p-multiselect.p-multiselect-chip .p-multiselect-token {
padding: 0.25rem 0.75rem;
margin-right: 0.5rem;
background: #dee2e6;
color: #212529;
border-radius: 16px;
}
.p-multiselect.p-multiselect-chip .p-multiselect-token .p-multiselect-token-icon {
margin-left: 0.5rem;
}
.p-multiselect .p-multiselect-trigger {
background: transparent;
color: #495057;
width: 2.357rem;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.p-multiselect.p-invalid.p-component {
border-color: #dc3545;
}
.p-inputwrapper-filled.p-multiselect.p-multiselect-chip .p-multiselect-label {
padding: 0.25rem 0.75rem;
}
.p-multiselect-panel {
background: #ffffff;
color: var(--grey);
border: 1px solid var(--fieldset-dark);
border-radius: 4px;
box-shadow: none;
}
.p-multiselect-panel .p-multiselect-header {
padding: 3px;
border-bottom: 1px solid #dee2e6;
color: #212529;
background: #efefef;
margin: 0;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-inputtext {
padding-right: 1.75rem;
}
.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-multiselect-filter-icon {
right: 0.75rem;
color: #495057;
}
.p-multiselect-panel .p-multiselect-header .p-checkbox {
margin-right: 0.5rem;
}
.p-multiselect-panel .p-multiselect-header .p-multiselect-close {
margin-left: 0.5rem;
width: 1rem;
height: 1rem;
color: #6c757d;
border: 0 none;
background: transparent;
border-radius: 50%;
transition: box-shadow 0.15s;
outline-color: transparent;
}
.p-multiselect-panel .p-multiselect-header .p-multiselect-close:enabled:hover {
color: #495057;
border-color: transparent;
background: transparent;
}
.p-multiselect-panel .p-multiselect-header .p-multiselect-close:focus-visible {
outline: 0 none;
outline-offset: 0;
box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
}
.p-multiselect-panel .p-multiselect-items {
padding: 0;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item {
margin: 0;
padding: 3px;
border: 0 none;
color: #212529;
background: transparent;
transition: box-shadow 0.15s;
border-radius: 0;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item:first-child {
margin-top: 0;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item:last-child {
margin-bottom: 0;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item.p-highlight {
/*color: #ffffff;*/
/*background: var(--button-primary-background);*/
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item.p-highlight.p-focus {
/*background: var(--button-primary-background);*/
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item:not(.p-highlight):not(.p-disabled).p-focus {
color: #212529;
background: #e9ecef;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item .p-checkbox {
margin-right: 0.5rem;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-item-group {
margin: 0;
padding: 0.75rem 1rem;
color: #212529;
background: #ffffff;
font-weight: 600;
}
.p-multiselect-panel .p-multiselect-items .p-multiselect-empty-message {
padding: 0.5rem 1.5rem;
color: #212529;
background: transparent;
}

37
resources/css/vue.css Normal file
View File

@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
.vueAction {
cursor: pointer;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.flex-align-center {
align-items: center;
}
.justify-self-start {
justify-self: start;
}
.gap-1 {
gap: 0.25rem;
}
.grid label {
padding-top: 5px;
}
.grid input[type=text] {
width: 100%;
}

View File

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2023 Andreas Palm
//
// SPDX-License-Identifier: AGPL-3.0-only
import {AxiosError} from 'axios';
export function AlertErrorHandler(error: AxiosError) {
if (error.response === undefined || error.response.status >= 500) {
console.log('Unknown error on axios request', error);
alert('Unerwarteter Fehler, weitere Hinweise ggf. in der JavaScript-Konsole');
} else {
console.log('ClientError on axios request', error);
alert(error.response.data);
}
}

7
resources/js/jqueryBridge.js vendored Normal file
View File

@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: 2023 Andreas Palm
//
// SPDX-License-Identifier: AGPL-3.0-only
export function reloadDataTables() {
window.$('#main .dataTable').DataTable().ajax.reload();
}

12
resources/js/vue.js Normal file
View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: 2023 Andreas Palm
//
// SPDX-License-Identifier: LicenseRef-EGPL-3.1
import '@res/css/vue.css';
import '@res/css/primevue/_base.css';
import {createApp} from "vue";
import PrimeVue from "primevue/config";
export function createVueApp(rootComponent, rootProps) {
return createApp(rootComponent, rootProps).use(PrimeVue);
}

View File

@ -0,0 +1,50 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: AGPL-3.0-only
-->
<script setup>
import {ref} from "vue";
import AutoComplete from "primevue/autocomplete";
import axios from "axios";
import SearchIcon from "primevue/icons/search";
const props = defineProps({
ajaxFilter: String,
modelValue: null,
forceSelection: Boolean
});
const emit = defineEmits(['update:modelValue']);
const items = ref([]);
async function search(event) {
await axios.get('index.php',
{
params: {
module: 'ajax',
action: 'filter',
filtername: props.ajaxFilter,
term: event.query,
object: true
}
})
.then(response => items.value = response.data)
}
</script>
<template>
<AutoComplete
:modelValue="modelValue"
@update:modelValue="value => emit('update:modelValue', value)"
:suggestions="items"
@complete="search"
dataKey="id"
:forceSelection="forceSelection"
dropdown
>
<template #dropdownicon>
<SearchIcon />
</template>
</AutoComplete>
</template>

30
vite.config.js Normal file
View File

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2023 Andreas Palm
//
// SPDX-License-Identifier: AGPL-3.0-only
import glob from 'glob';
import path from 'path';
import vue from '@vitejs/plugin-vue';
const moduleInputs = glob.sync('classes/Modules/*/www/js/entry.{js,jsx}')
.map(file => ['modules/'+file.split('/')[2], file]);
/** @type {import('vite').UserConfig} */
export default {
build: {
rollupOptions: {
input: {
...Object.fromEntries(moduleInputs)
}
},
manifest: true,
outDir: 'www/dist',
},
plugins: [vue()],
mode: 'development',
resolve: {
alias: {
'@res': path.resolve(__dirname, 'resources')
}
}
}

1
www/dist/assets/entry-078d65ea.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
www/dist/manifest.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"classes/Modules/MatrixProduct/www/js/entry.css": {
"file": "assets/entry-078d65ea.css",
"src": "classes/Modules/MatrixProduct/www/js/entry.css"
},
"classes/Modules/MatrixProduct/www/js/entry.jsx": {
"css": [
"assets/entry-078d65ea.css"
],
"file": "assets/modules/MatrixProduct-2b98726c.js",
"isEntry": true,
"src": "classes/Modules/MatrixProduct/www/js/entry.jsx"
}
}

View File

@ -15853,127 +15853,6 @@ function Gegenkonto($ust_befreit,$ustid='', $doctype = '', $doctypeId = 0)
$this->app->Tpl->Parse('PAGE','emptytab.tpl');
}
/**
* @param int $id
*
* @return string
*
*/
public function GetTrackingRawLink($id)
{
return $this->GetTrackinglink($id, true);
}
/**
* @param int $id
* @param bool $returnRaw
*
* @return string
*/
public function GetTrackinglink($id, $returnRaw = false)
{
if($id > 0)
{
$versandarr = $this->app->DB->SelectRow("SELECT * FROM versand WHERE id='$id' LIMIT 1");
}
if(empty($versandarr))
{
return '';
}
$adresse = $versandarr['adresse'];
$lieferscheinid = $versandarr['lieferschein'];
if($lieferscheinid > 0){
$lieferscheinarr = $this->app->DB->SelectRow("SELECT auftragid,projekt FROM lieferschein WHERE id='$lieferscheinid' LIMIT 1");
}
if(!empty($lieferscheinarr))
{
$auftrag = $lieferscheinarr['auftragid'];
$projekt = $lieferscheinarr['projekt'];
}else{
$auftrag = 0;
$projekt = 0;
}
$auftragarr = $this->app->DB->SelectRow("SELECT belegnr,internet,ihrebestellnummer,DATE_FORMAT(datum,'%d.%m.%Y') as datum_de FROM auftrag WHERE id='$auftrag' LIMIT 1");
if(!empty($auftragarr)){
$auftragbelegnr = $auftragarr['belegnr'];
$auftraginternet = $auftragarr['internet'];
$ihrebestellnummer = $auftragarr['ihrebestellnummer'];
$auftragdatum = $auftragarr['datum_de'];
}else{
$auftragbelegnr = '';
$auftraginternet = '';
$ihrebestellnummer = '';
$auftragdatum = '';
}
$tracking = $versandarr['tracking'];
$versandunternehmen = $versandarr['versandunternehmen'];
// FIX fuer selbstabholer Mail
$versandart = $versandarr['versandart'];
if($versandart=='selbstabholer') {
$versandunternehmen='selbstabholer';
}
if($versandunternehmen=='dhl' || $versandunternehmen=="dhlpremium" || $versandunternehmen=="intraship"){
$versandmodul = false;
}
$typ = $versandunternehmen;
if($typ === ''){
$typ = $versandart;
}
//$versandartenmodul = $this->app->DB->SelectArr("SELECT id, modul FROM versanddienstleister WHERE aktiv = 1 AND modul = '".$this->app->DB->real_escape_string($typ)."' AND (projekt = 0 OR projekt = '$projekt') ORDER BY projekt DESC LIMIT 1");
$versandartenmodul = $this->app->DB->SelectArr("SELECT * FROM versandarten WHERE aktiv = 1 AND ausprojekt = 0 AND modul != '' AND type = '".$this->app->DB->real_escape_string($typ)."' AND modul != '' AND (projekt = 0 OR projekt = '$projekt') ORDER BY projekt DESC LIMIT 1");
$standard = true;
if($versandartenmodul && @is_file(dirname(__FILE__).'/versandarten/'.$versandartenmodul[0]['modul'].'.php'))
{
$obj = $this->LoadVersandModul($versandartenmodul[0]['modul'], $versandartenmodul[0]['id']);
if(!empty($obj) && method_exists($obj, 'Trackinglink'))
{
if($obj->Trackinglink($tracking, $notsend, $link, $rawlink))
{
if($returnRaw) {
return $rawlink;
}
return $link;
}
}
}elseif($versandartenmodul2 = $this->app->DB->SelectArr("SELECT * FROM versandarten WHERE aktiv = 1 AND ausprojekt = 0 AND type = '".$this->app->DB->real_escape_string($typ)."' AND (projekt = 0 OR projekt = '$projekt') ORDER BY projekt DESC LIMIT 1"))
{
$obj = $this->LoadVersandModul($versandartenmodul2[0]['modul'], $versandartenmodul2[0]['id']);
if(!empty($obj) && method_exists($obj, 'Trackinglink'))
{
if($obj->Trackinglink($tracking, $notsend, $link, $rawlink))
{
if($returnRaw) {
return $rawlink;
}
return $link;
}
}
}
if(!$versandmodul && $standard)
{
if($versandunternehmen=="dhl" || $versandunternehmen=="dhlpremium" || $versandunternehmen=="intraship")
{
return 'http://nolp.dhl.de/nextt-online-public/set_identcodes.do?lang=de&idc='.$tracking;
}
else if ($versandunternehmen=="logoix")
{
return 'http://www.logoix.com/cgi-bin/tnt.pl?q='.$tracking;
}
else if ($versandunternehmen=="dpd")
{
return 'https://tracking.dpd.de/parcelstatus/?locale=de_DE&query='.$tracking;
}
else if ($versandunternehmen=="gls")
{
return 'https://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/DE03/DE/5004.htm?txtRefNo='.$tracking;
}
}
}
/**
* @param int $id
*/

View File

@ -1,6 +1,6 @@
<?php
/*
* SPDX-FileCopyrightText: 2022 Andreas Palm
* SPDX-FileCopyrightText: 2022-2024 Andreas Palm
* SPDX-FileCopyrightText: 2019 Xentral (c) Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg, Germany
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
@ -20,6 +20,10 @@
*/
?>
<?php
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
use Xentral\Modules\Onlineshop\Data\Shipment;
use Xentral\Modules\Onlineshop\Data\ShopConnectorResponseInterface;
class Remote
@ -1886,7 +1890,6 @@ class Remote
if(is_numeric($matrixPseudoStorage) && $matrixPseudoStorage < 0) {
$matrixPseudoStorage = 0;
}
$matrixStock = (float)$this->app->erp->ArtikelAnzahlVerkaufbar($eigenschaft['artikel'], 0, $projektlager, $id, $lagergrundlage);
if($matrixStock < 0) {
$matrixStock = 0;
}
@ -2301,133 +2304,58 @@ class Remote
return $result;
}
public function getDataToSendForUpdateOrder(int $shopId, int $orderId): array
public function getDataToSendForUpdateOrder(int $shopId, int $orderId): ?OrderStatusUpdateRequest
{
$orderArr = $this->app->DB->SelectRow("SELECT * FROM `auftrag` WHERE `id` = {$orderId} LIMIT 1");
$status = $orderArr['status'];
$zahlungsweise = $orderArr['zahlungsweise'];
$shopextid = $orderArr['shopextid'];
$internet = $orderArr['internet'];
$deliveryNoteArr = $this->app->DB->SelectRow(
"SELECT `id`, `versandart` FROM `lieferschein` WHERE `auftragid` = {$orderId} LIMIT 1"
);
$trackingArr = null;
$versandart = '';
$tracking = '';
$shippingProduct = null;
if(!empty($deliveryNoteArr)) {
$deliveryNoteId = $deliveryNoteArr['id'];
$versandart = $deliveryNoteArr['versandart'];
$query =
"SELECT *
FROM `shopexport_versandarten`
WHERE `aktiv`=1 AND `versandart_wawision` = '{$versandart}' AND `shop` = {$shopId} AND `versandart_wawision` <> ''
LIMIT 1";
$shippingMapping = $this->app->DB->SelectRow($query);
$versandartAusgehend = $shippingMapping['versandart_ausgehend'] ?? null;
$shippingProduct = $shippingMapping['produkt_ausgehend'] ?? null;
$orderArr = $this->app->DB->SelectRow("SELECT `zahlungsweise`, `shopextid` FROM `auftrag` WHERE `id` = $orderId LIMIT 1");
if (empty($orderArr))
return null;
if(!empty($versandartAusgehend)){
$versandart = $versandartAusgehend;
}
$trackingArr = $this->app->DB->SelectPairs(
sprintf(
"SELECT `id`, `tracking`
FROM `versand`
WHERE `lieferschein` = {$deliveryNoteId} AND `tracking` <> ''
ORDER BY `id` DESC"
)
);
$tracking = '';
if(!empty($trackingArr)) {
$tracking = reset($trackingArr);
$data = new OrderStatusUpdateRequest();
$data->orderId = $orderId;
$data->shopOrderId = $orderArr['shopextid'];
$statusArr = $this->app->DB->SelectFirstCols("SELECT DISTINCT status FROM auftrag WHERE id = $orderId OR teillieferungvon = $orderId");
if (in_array('storniert', $statusArr))
$data->orderStatus = OrderStatus::Cancelled;
if (in_array('abgeschlossen', $statusArr))
$data->orderStatus = OrderStatus::Completed;
if (in_array('freigegeben', $statusArr))
$data->orderStatus = OrderStatus::InProgress;
if (in_array('angelegt', $statusArr))
$data->orderStatus = OrderStatus::Imported;
$sql = "
SELECT
v.id,
v.tracking,
v.tracking_link,
COALESCE(sv.versandart_ausgehend, sv.versandart_shop, v.versandart) versandart
FROM
auftrag a
LEFT JOIN lieferschein l ON
l.auftragid = a.id
LEFT JOIN lieferschein_position lp ON
lp.lieferschein = l.id
LEFT JOIN versandpaket_lieferschein_position vlp ON
vlp.lieferschein_position = lp.id
LEFT JOIN versandpakete v ON
vlp.versandpaket = v.id OR v.lieferschein_ohne_pos = l.id
LEFT JOIN shopexport_versandarten sv ON
sv.versandart_wawision = v.versandart AND sv.shop = $shopId
WHERE a.id = $orderId OR a.teillieferungvon = $orderId
ORDER BY v.id";
$shipments = $this->app->DB->SelectArr($sql);
foreach ($shipments as $shipment) {
$item = new Shipment();
$item->id = $shipment['id'];
$item->trackingNumber = $shipment['tracking'];
$item->trackingUrl = $shipment['tracking_link'];
$item->shippingMethod = $shipment['versandart'];
$data->shipments[] = $item;
}
$positionen = $this->app->DB->SelectArr(
"SELECT ap.webid, trim(lp.geliefert)+0 AS `geliefert`, trim(lp.menge)+0 AS `menge`, lp.id
FROM `lieferschein_position` AS `lp`
INNER JOIN `lieferschein` AS `l` ON l.id = lp.lieferschein
INNER JOIN `auftrag` AS `a` ON a.id = l.auftragid
INNER JOIN `auftrag_position` AS `ap` ON ap.id = lp.auftrag_position_id
WHERE l.id = {$deliveryNoteId} AND ap.webid <> '' "
);
$allPositions = false;
if(!empty($positionen)) {
$allPositions = true;
foreach($positionen as $position) {
if($position['geliefert'] > 0) {
$itemlist[] = array('webid'=>$position['webid'],'quantity'=>$position['geliefert']);
if($position['geliefert'] < $position['menge']) {
$allPositions = false;
}
}
elseif($this->app->DB->Select("SELECT trim(sum(geliefert))+0
FROM lieferschein_position
WHERE explodiert_parent = '".$position['id']."' AND lieferschein = '$deliveryNoteId'")) {
$itemlist[] = array('webid'=>$position['webid'],'quantity'=>$position['menge']);
}
else {
$allPositions = false;
}
}
if($allPositions && (!empty($itemlist)?count($itemlist):0) <
$this->app->DB->Select(
sprintf('SELECT count(id) FROM auftrag_position WHERE auftrag = %d', $orderId)
)
) {
$allPositions = false;
}
}
}
if(!empty($itemlist)) {
$data['itemlist'] = $itemlist;
if($allPositions) {
$data['allpositions'] = 1;
}
}
$data['orderId'] = $orderId;
$data['auftrag'] = $shopextid;
$data['internet'] = $internet;
$data['zahlungsweise'] = $zahlungsweise;
$data['versandart'] = $versandart;
if(!empty($trackingArr)) {
$data['trackinglist'] = $trackingArr;
}
if($status==='abgeschlossen') {
$data['versand']='1';
$data['zahlung']='1';
if($shippingProduct !== null) {
$data['shipping_product'] = $shippingProduct;
}
if($tracking!='') {
$data['tracking']=$tracking;
$lastShippingId = (int)$this->app->DB->Select(
sprintf(
"SELECT `id` FROM `versand` WHERE `lieferschein` = %d AND `lieferschein` > 0
ORDER BY `id` DESC LIMIT 1",
$deliveryNoteId
)
);
$trackinglink = $lastShippingId > 0 && method_exists($this->app->erp,'GetTrackinglink')
?$this->app->erp->GetTrackinglink($lastShippingId):'';
if($trackinglink) {
$data['trackinglink'] = $trackinglink;
if(!empty($trackingArr)) {
foreach($trackingArr as $versandId => $track) {
$data['trackinglinklist'][$versandId] = $this->app->erp->GetTrackinglink($versandId);
}
}
}
$trackinglinkRaw = $lastShippingId > 0 && method_exists($this->app->erp,'GetTrackingRawLink')
?$this->app->erp->GetTrackingRawLink($lastShippingId):'';
if(!empty($trackinglinkRaw)) {
$data['trackinglinkraw'] = $trackinglinkRaw;
}
}
}
return $data;
return $data;
}
/**
@ -2438,9 +2366,10 @@ class Remote
*/
public function RemoteUpdateAuftrag($shopId, $orderId)
{
$data = $this->getDataToSendForUpdateOrder((int)$shopId, (int)$orderId);
if($data['versand']=='1' || $data['zahlung']=='1')
{
$data = $this->getDataToSendForUpdateOrder((int)$shopId, (int)$orderId);
if($data?->orderStatus !== OrderStatus::Completed)
return;
$bearbeiter = 'Cronjob';
if(isset($this->app->User)){
$bearbeiter = $this->app->DB->real_escape_string($this->app->User->GetName());
@ -2465,7 +2394,6 @@ class Remote
$this->app->erp->AuftragProtokoll($orderId, 'Versandmeldung an Shop &uuml;bertragen', $bearbeiter);
$this->app->DB->Update("UPDATE `auftrag` SET `shopextstatus` = 'abgeschlossen' WHERE `id` = $orderId LIMIT 1");
}
}
/**

View File

@ -57,12 +57,6 @@ abstract class Versanddienstleister
if ($rechnungId <= 0)
$rechnungId = $this->app->DB->Select("SELECT rechnungid FROM lieferschein WHERE id='$lieferscheinId' LIMIT 1");
}
if ($sid === 'versand') {
$versandId = $id;
$lieferscheinId = $this->app->DB->Select("SELECT lieferschein FROM versand WHERE id='$versandId' LIMIT 1");
$rechnungId = $this->app->DB->Select("SELECT rechnung FROM versand WHERE id='$versandId' LIMIT 1");
$sid = 'lieferschein';
}
if ($auftragId <= 0 && $rechnungId > 0)
$auftragId = $this->app->DB->Select("SELECT auftragid FROM rechnung WHERE id=$rechnungId LIMIT 1");

View File

@ -1,4 +1,12 @@
<?php
/*
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*
@ -1201,6 +1209,7 @@ class Ajax {
$rmodule = $this->app->Secure->GetGET('rmodule');
$raction = $this->app->Secure->GetGET('raction');
$rid = (int)$this->app->Secure->GetGET('rid');
$asObject = $this->app->Secure->GetGET('object');
$pruefemodule = array('artikel','auftrag','angebot','rechnung','lieferschein','gutschrift','bestellung','produktion');
$filter_projekt = 0;
if($raction === 'edit' && $rid && in_array($rmodule, $pruefemodule))
@ -1600,6 +1609,9 @@ select a.kundennummer, (SELECT name FROM adresse a2 WHERE a2.kundennummer = a.ku
$newarr[] = $arr[$i]['iso'];
}
break;
case "activelanguages":
$newarr = $this->app->DB->SelectArr('SELECT * FROM sprachen WHERE aktiv=1');
break;
case "geschaeftsbrief_vorlagen":
$arr = $this->app->DB->SelectArr("SELECT CONCAT(id,' ',subjekt,' (',sprache,')') as name FROM geschaeftsbrief_vorlagen");
$carr = !empty($arr)?count($arr):0;
@ -2520,15 +2532,19 @@ select a.kundennummer, (SELECT name FROM adresse a2 WHERE a2.kundennummer = a.ku
//if($checkprojekt > 0 && $eigenernummernkreis=="1") $tmp_where = " AND projekt='$checkprojekt' ";
//else $tmp_where = "";
$selectfields = $asObject ? 'art.id, art.nummer, art.name_de name' : "CONCAT(nummer,' ',name_de) as `name`";
$arr = $this->app->DB->SelectArr(
"SELECT CONCAT(nummer,' ',name_de) as `name`
FROM artikel AS art WHERE geloescht=0 AND ($subwhere) AND geloescht=0 AND intern_gesperrt!=1 $tmp_where ".
"SELECT $selectfields
FROM artikel AS art WHERE geloescht=0 AND ($subwhere) AND intern_gesperrt!=1 $tmp_where ".
$this->app->erp->ProjektRechte('art.projekt'). ' LIMIT 20'
);
$carr = !empty($arr)?count($arr):0;
for($i = 0; $i < $carr; $i++) {
$newarr[] = $arr[$i]['name'];
if ($asObject) {
$newarr = $arr;
} else {
$carr = !empty($arr) ? count($arr) : 0;
for ($i = 0; $i < $carr; $i++) {
$newarr[] = $arr[$i]['name'];
}
}
break;
@ -3861,11 +3877,16 @@ select a.kundennummer, (SELECT name FROM adresse a2 WHERE a2.kundennummer = a.ku
}
break;
case "projektname":
$arr = $this->app->DB->SelectArr("SELECT CONCAT(p.abkuerzung,' ',p.name) as name FROM projekt p WHERE p.geloescht=0 AND status <> 'abgeschlossen' AND (p.name LIKE '%$term%' OR p.name LIKE '%$term2%' OR p.name LIKE '%$term3%' OR p.abkuerzung LIKE '%$term%' OR p.abkuerzung LIKE '%$term2%' OR p.abkuerzung LIKE '%$term3%') ".$this->app->erp->ProjektRechte());
$carr = !empty($arr)?count($arr):0;
for($i = 0; $i < $carr; $i++) {
$newarr[] = $arr[$i]['name'];
case "projektname":
$fields = $asObject ? 'p.id, p.abkuerzung, p.name' : "CONCAT(p.abkuerzung,' ',p.name) as name";
$arr = $this->app->DB->SelectArr("SELECT $fields FROM projekt p WHERE p.geloescht=0 AND status <> 'abgeschlossen' AND (p.name LIKE '%$term%' OR p.name LIKE '%$term2%' OR p.name LIKE '%$term3%' OR p.abkuerzung LIKE '%$term%' OR p.abkuerzung LIKE '%$term2%' OR p.abkuerzung LIKE '%$term3%') ".$this->app->erp->ProjektRechte());
if ($asObject) {
$newarr = $arr;
} else {
$carr = !empty($arr) ? count($arr) : 0;
for ($i = 0; $i < $carr; $i++) {
$newarr[] = $arr[$i]['name'];
}
}
break;
@ -4179,7 +4200,16 @@ select a.kundennummer, (SELECT name FROM adresse a2 WHERE a2.kundennummer = a.ku
}else{
$cnewarr = !empty($newarr)?count($newarr):0;
for($i=0;$i<$cnewarr;$i++) {
$tmp[] = $this->app->erp->ClearDataBeforeOutput(html_entity_decode($newarr[$i], ENT_QUOTES, 'UTF-8'));
$row = $newarr[$i];
if (is_string($row))
$tmp[] = $this->app->erp->ClearDataBeforeOutput(html_entity_decode($newarr[$i], ENT_QUOTES, 'UTF-8'));
else if (is_array($row)) {
$tmprow = [];
foreach ($row as $key => $value) {
$tmprow[$key] = $this->app->erp->ClearDataBeforeOutput(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
}
$tmp[] = $tmprow;
}
}
}

View File

@ -1,4 +1,12 @@
<?php
/*
* SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
* SPDX-FileCopyrightText: 2023 Andreas Palm
*
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*
@ -23,6 +31,7 @@ class Artikel extends GenArtikel {
/** @var Application $app */
var $app;
const MODULE_NAME = 'Article';
protected \Xentral\Modules\Article\Service\ArticleService $service;
public function TableSearch($app, $name, $erlaubtevars)
{
@ -1885,6 +1894,7 @@ class Artikel extends GenArtikel {
if($intern){
return;
}
$this->service = $this->app->Container->get('ArticleService');
$this->app->ActionHandlerInit($this);
@ -5205,157 +5215,11 @@ class Artikel extends GenArtikel {
$freifelderuebersetzung = 0;
}
$data = array($id, $einkaufspreise, $verkaufspreise, $dateien, $eigenschaften, $anweisungen, $stuecklisten, $freifelderuebersetzung);
$idnew = $this->ArtikelCopy($data, true);
$idnew = $this->service->CopyArticle($id, $einkaufspreise, $verkaufspreise, $dateien, $eigenschaften, $anweisungen, $stuecklisten, $freifelderuebersetzung);
echo json_encode(array('status'=>1,'url'=>'index.php?module=artikel&action=edit&id='.$idnew));
$this->app->ExitXentral();
}
function ArtikelCopy($data = null, $return = false)
{
//$id = $this->app->Secure->GetGET("id");
$id = $data[0];
$einkaufspreise = $data[1];
$verkaufspreise = $data[2];
$dateien = $data[3];
$eigenschaften = $data[4];
$anweisungen = $data[5];
$stuecklisten = $data[6];
$freifelderuebersetzung = $data[7];
$this->app->DB->MysqlCopyRow('artikel','id',$id);
$idnew = $this->app->DB->GetInsertID();
$steuersatz = $this->app->DB->Select("SELECT steuersatz FROM artikel WHERE id = '$id' LIMIT 1");
if($steuersatz == ''){
$steuersatz = -1.00;
$this->app->DB->Update("UPDATE artikel SET steuersatz = '$steuersatz' WHERE id = '$idnew' LIMIT 1");
}
$this->app->DB->Update("UPDATE artikel SET nummer='' WHERE id='$idnew' LIMIT 1");
if($this->app->DB->Select("SELECT variante_kopie FROM artikel WHERE id = '$id' LIMIT 1"))$this->app->DB->Update("UPDATE artikel SET variante = 1, variante_von = '$id' WHERE id = '$idnew' LIMIT 1");
if($stuecklisten == 1){
// wenn stueckliste
$stueckliste = $this->app->DB->Select("SELECT stueckliste FROM artikel WHERE id='$id' LIMIT 1");
if($stueckliste==1)
{
$artikelarr = $this->app->DB->SelectArr("SELECT * FROM stueckliste WHERE stuecklistevonartikel='$id'");
$cartikelarr = $artikelarr?count($artikelarr):0;
for($i=0;$i<$cartikelarr;$i++)
{
$sort = $artikelarr[$i]['sort'];
$artikel = $artikelarr[$i]['artikel'];
$referenz = $artikelarr[$i]['referenz'];
$place = $artikelarr[$i]['place'];
$layer = $artikelarr[$i]['layer'];
$stuecklistevonartikel = $idnew;
$menge = $artikelarr[$i]['menge'];
$firma = $artikelarr[$i]['firma'];
$this->app->DB->Insert("INSERT INTO stueckliste (id,sort,artikel,referenz,place,layer,stuecklistevonartikel,menge,firma) VALUES
('','$sort','$artikel','$referenz','$place','$layer','$stuecklistevonartikel','$menge','$firma')");
}
}
}
/*$arbeitsanweisungen = $this->app->DB->SelectArr("SELECT id FROM `artikel_arbeitsanweisung` WHERE artikel = '$id'");
if($arbeitsanweisungen)
{
foreach($arbeitsanweisungen as $arbeitsanweisung)
{
$newarbeitsanweisung = $this->app->DB->MysqlCopyRow('artikel_arbeitsanweisung', 'id', $arbeitsanweisung['id']);
$this->app->DB->Update("UPDATE `artikel_arbeitsanweisung` SET artikel = '$idnew' WHERE id = '$newarbeitsanweisung' LIMIT 1");
}
}*/
//TODO hinweis es wuren keine Preise kopiert
if($einkaufspreise == 1){
$einkaufspreise = $this->app->DB->SelectArr("SELECT id FROM einkaufspreise WHERE artikel = '$id'");
if($einkaufspreise){
foreach($einkaufspreise as $preis){
$neuereinkaufspreis = $this->app->DB->MysqlCopyRow("einkaufspreise", "id", $preis['id']);
$this->app->DB->Update("UPDATE einkaufspreise SET artikel = '$idnew' WHERE id = '$neuereinkaufspreis' LIMIT 1");
}
}
}
if($verkaufspreise == 1){
$verkaufspreise = $this->app->DB->SelectArr("SELECT id FROM verkaufspreise WHERE artikel = '$id'");
if($verkaufspreise){
foreach($verkaufspreise as $preis){
$neuerverkaufspreis = $this->app->DB->MysqlCopyRow("verkaufspreise", "id", $preis['id']);
$this->app->DB->Update("UPDATE verkaufspreise SET artikel = '$idnew' WHERE id = '$neuerverkaufspreis' LIMIT 1");
}
}
}
if($dateien == 1){
$dateien = $this->app->DB->SelectArr("SELECT DISTINCT datei FROM datei_stichwoerter WHERE parameter = '$id' AND objekt = 'Artikel'");
$datei_stichwoerter = $this->app->DB->SelectArr("SELECT id,datei FROM datei_stichwoerter WHERE parameter = '$id' AND objekt = 'Artikel'");
if($dateien){
foreach($dateien as $datei){
$titel = $this->app->DB->Select("SELECT titel FROM datei WHERE id='".$datei['datei']."' LIMIT 1");
$beschreibung = $this->app->DB->Select("SELECT beschreibung FROM datei WHERE id='".$datei['datei']."' LIMIT 1");
$nummer = $this->app->DB->Select("SELECT nummer FROM datei WHERE id='".$datei['datei']."' LIMIT 1");
$name = $this->app->DB->Select("SELECT dateiname FROM datei_version WHERE datei='".$this->app->DB->real_escape_string($datei['datei'])."' ORDER by version DESC LIMIT 1");
$ersteller = $this->app->User->GetName();
$tmpnewdateiid = $this->app->erp->CreateDatei($name,$titel,$beschreibung,$nummer,$this->app->erp->GetDateiPfad($datei['datei']),$ersteller);
$datei_mapping[$datei['datei']] = $tmpnewdateiid;
}
}
if($datei_stichwoerter){
foreach($datei_stichwoerter as $datei){
$neuesstichwort = $this->app->DB->MysqlCopyRow("datei_stichwoerter", "id", $datei['id']);
$newdatei = $datei_mapping[$datei['datei']];
$this->app->DB->Update("UPDATE datei_stichwoerter SET datei='$newdatei', parameter = '$idnew', objekt = 'Artikel' WHERE id = '$neuesstichwort' LIMIT 1");
}
}
}
if($eigenschaften == 1){
$aeigenschaften = $this->app->DB->SelectArr("SELECT id FROM artikeleigenschaftenwerte WHERE artikel = '$id'");
if($aeigenschaften){
foreach($aeigenschaften as $eigenschaft){
$neue_eigenschaft = $this->app->DB->MysqlCopyRow("artikeleigenschaftenwerte", "id", $eigenschaft['id']);
$this->app->DB->Update("UPDATE artikeleigenschaftenwerte SET artikel = '$idnew' WHERE id = '$neue_eigenschaft' LIMIT 1");
}
}
}
//WERDEN AUCH SCHON IMMER KOPIERT
if($anweisungen == 1){
$arbeitsanweisungen = $this->app->DB->SelectArr("SELECT id FROM artikel_arbeitsanweisung WHERE artikel = '$id'");
if($arbeitsanweisungen){
foreach($arbeitsanweisungen as $anweisung){
$neue_anweisung = $this->app->DB->MysqlCopyRow("artikel_arbeitsanweisung", "id", $anweisung['id']);
$this->app->DB->Update("UPDATE artikel_arbeitsanweisung SET artikel = '$idnew' WHERE id = '$neue_anweisung' LIMIT 1");
}
}
}
if($freifelderuebersetzung == 1){
$freifelderuebersetzungen = $this->app->DB->SelectArr("SELECT id FROM artikel_freifelder WHERE artikel = '$id'");
if($freifelderuebersetzungen){
$this->app->DB->Insert("INSERT INTO artikel_freifelder (artikel, sprache, nummer, wert) SELECT '$idnew', sprache, nummer, wert FROM artikel_freifelder WHERE artikel = '$id'");
}
}
// artikelbilder kopieren
if($return){
return $idnew;
}
// eventuell einkaufspreise verkaufspreise und stueckliste kopieren?
$msg = $this->app->erp->base64_url_encode("<div class=error>Sie befinden sich in der neuen Kopie des Artikels. Bitte legen Sie Verkaufs- und Einkaufspreise und Bilder bzw. Dateien an! Diese wurden nicht kopiert!</div>");
$this->app->Location->execute("index.php?module=artikel&action=edit&msg=$msg&id=".$idnew);
}
public function ArtikelProjekte()
{
$this->app->Tpl->Add('UEBERSCHRIFT',' (Projekte)');
@ -7333,6 +7197,10 @@ class Artikel extends GenArtikel {
$this->app->erp->MenuEintrag('index.php?module=artikel&action=eigenschaften&id='.$id, 'Eigenschaften');
}
if ($tmp[0]['matrixprodukt']==1) {
$this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=artikel&id=$id", 'Matrixprodukt');
}
if($rabatt!='1'){
$this->app->erp->MenuEintrag("index.php?module=artikel&action=einkauf&id=$id",'Einkauf');
if($this->app->erp->RechteVorhanden('einkaufabgleich','einkaufapi'))

View File

@ -0,0 +1,58 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<div id="tabs">
<ul>
<li><a href="#tabs-1">Gruppen/Optionen</a></li>
<li><a href="#tabs-2">Variantenzuordnung</a></li>
</ul>
<div id="tabs-1">
<div class="row">
<div class="col-xs-12 col-md-10 col-md-height">
<div class="inside-white inside-full-height">
[MESSAGE]
[TAB1]
</div>
</div>
<div class="col-xs-12 col-md-2 col-md-height">
<div class="inside inside-full-height">
<fieldset>
<legend>{|Aktionen|}</legend>
<input type="button" class="btnGreenNew vueAction" data-action="addGlobalToArticle" data-article-id="[ID]" value="&#10010; Optionen übernehmen">
<input type="button" class="btnGreenNew vueAction" data-action="[ADDEDITFUNCTION]" data-group-id="[SID]" data-article-id="[ID]" value="&#10010; Neuer Eintrag">
<input type="button" class="btnGreenNew" name="matrixprodukt_module"
value="&#10140; Vordefinierte Gruppen/Optionen" onclick="window.location.href='index.php?module=matrixprodukt&action=list';">
</fieldset>
</div>
</div>
</div>
</div>
<div id="tabs-2">
<div class="row">
<div class="row-height">
<div class="col-xs-12 col-md-10 col-md-height">
<div class="inside-white inside-full-height">
[MESSAGE2]
[TAB2]
</div>
</div>
<div class="col-xs-12 col-md-2 col-md-height" [TAB2HIDEACTIONS]>
<div class="inside inside-full-height">
<fieldset>
<legend>{|Aktionen|}</legend>
<input type="button" class="btnGreenNew vueAction" data-action="variantEdit" data-article-id="[ID]" value="&#10010; Neue Variante">
<input type="button" class="btnGreenNew vueAction" data-action="createMissing" data-article-id="[ID]" value="&#10010; Erzeuge fehlende Varianten">
</fieldset>
</div>
</div>
</div>
</div>
</div>
<!-- tab view schließen -->
</div>
<!-- ende tab view schließen -->
<div id="vueapp"></div>

View File

@ -1,5 +1,8 @@
<!-- gehort zu tabview -->
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2019 Xentral (c) Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg, Germany
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<div id="tabs">
<ul>
<li><a href="#tabs-1">[TABTEXT]</a></li>
@ -8,20 +11,12 @@
<div id="tabs-1">
<div class="row">
<div class="row-height">
<div class="col-xs-12 col-md-10 col-md-height">
<div class="col-xs-12 col-md-height">
<div class="inside-white inside-full-height">
[MESSAGE]
[TAB1]
</div>
</div>
<div class="col-xs-12 col-md-2 col-md-height">
<div class="inside inside-full-height">
<fieldset>
<legend>{|Aktionen|}</legend>
<input type="button" class="btnGreenNew" name="neueuebersetzung" value="&#10010; Neue &Uuml;bersetzung" onclick="MatrixproduktUebersetzungEdit(0);">
</fieldset>
</div>
</div>
</div>
</div>
</div>
@ -29,359 +24,5 @@
</div>
<!-- ende tab view schließen -->
<div id="editMatrixprodukt" style="display:none;" title="Bearbeiten">
<form action="" method="post" name="eprooform">
<input type="hidden" id="matrixprodukt_id">
<fieldset>
<legend>{|Einstellungen|}</legend>
<table>
<tr>
<td width="100">{|Name|}:</td>
<td><input type="text" size="40" name="matrixprodukt_name" id="matrixprodukt_name"></td>
</tr>
<tr[STYLEEXT]>
<td>{|Name Extern|}:</td>
<td><input type="text" size="40" name="matrixprodukt_name_ext" id="matrixprodukt_name_ext"></td>
</tr>
<tr>
<td>{|Projekt|}:</td>
<td><input type="text" size="40" name="matrixprodukt_projekt" id="matrixprodukt_projekt"></td>
</tr>
<tr>
<td>{|Aktiv|}:</td>
<td><input type="checkbox" name="matrixprodukt_aktiv" id="matrixprodukt_aktiv" value="1"></td>
</tr>
</table>
</fieldset>
</form>
</div>
<div id="editMatrixproduktUebersetzung" style="display:none;" title="Bearbeiten">
<form action="" method="post" name="eprooform">
<input type="hidden" id="matrixprodukt_uebersetzung_id">
<fieldset>
<legend>{|Von|}</legend>
<table>
<tr>
<td width="100">{|Sprache|}:</td>
<td><select name="matrixprodukt_sprache_von" id="matrixprodukt_sprache_von">
[SPRACHEN]
</select></td>
</tr>
<tr>
<td>{|Name|}:</td>
<td><input type="text" size="40" name="matrixprodukt_name_von" id="matrixprodukt_name_von"></td>
</tr>
<tr>
<td>{|Name Extern|}:</td>
<td><input type="text" size="40" name="matrixprodukt_name_ext_von" id="matrixprodukt_name_ext_von"></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>{|Nach|}</legend>
<table>
<tr>
<td width="100">{|Sprache|}:</td>
<td><select name="matrixprodukt_sprache_nach" id="matrixprodukt_sprache_nach">
[SPRACHEN]
</select></td>
</tr>
<tr>
<td>{|Name|}:</td>
<td><input type="text" size="40" name="matrixprodukt_name_nach" id="matrixprodukt_name_nach"></td>
</tr>
<tr>
<td>{|Name Extern|}:</td>
<td><input type="text" size="40" name="matrixprodukt_name_ext_nach" id="matrixprodukt_name_ext_nach"></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>{|Einstellungen|}</legend>
<table>
<tr>
<td width="100">{|Projekt|}:</td>
<td><input type="text" size="40" name="matrixprodukt_uebersetzung_projekt" id="matrixprodukt_uebersetzung_projekt"></td>
</tr>
<tr>
<td>{|Aktiv|}:</td>
<td><input type="checkbox" name="matrixprodukt_uebersetzung_aktiv" id="matrixprodukt_uebersetzung_aktiv" value="1"></td>
</tr>
</table>
</fieldset>
</form>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#matrixprodukt_name').focus();
$("#editMatrixprodukt").dialog({
modal: true,
bgiframe: true,
closeOnEscape:false,
minWidth:500,
maxHeight:800,
autoOpen: false,
buttons: {
ABBRECHEN: function() {
MatrixproduktReset();
$(this).dialog('close');
},
SPEICHERN: function() {
MatrixproduktEditSave();
}
}
});
$("#editMatrixprodukt").dialog({
close: function( event, ui ) {MatrixproduktReset();}
});
$("#editMatrixproduktUebersetzung").dialog({
modal: true,
bgiframe: true,
closeOnEscape:false,
minWidth:500,
maxHeight:800,
autoOpen: false,
buttons: {
ABBRECHEN: function() {
MatrixproduktUebersetzungReset();
$(this).dialog('close');
},
SPEICHERN: function() {
MatrixproduktUebersetzungEditSave();
}
}
});
$("#editMatrixproduktUebersetzung").dialog({
close: function( event, ui ) {MatrixproduktUebersetzungReset();}
});
});
function MatrixproduktUebersetzungReset(){
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_uebersetzung_id').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_sprache_von').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_von').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_ext_von').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_sprache_nach').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_nach').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_ext_nach').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_uebersetzung_projekt').val('');
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_uebersetzung_aktiv').prop("checked", true);
var languageFrom = document.getElementById('matrixprodukt_sprache_von');
languageFrom.selectedIndex = 0;
var languageTo = document.getElementById('matrixprodukt_sprache_nach');
languageTo.selectedIndex = 0;
}
function MatrixproduktUebersetzungEditSave(){
$.ajax({
url: 'index.php?module=matrixprodukt&action=list&cmd=uebersetzungsave',
data: {
//Alle Felder die fürs editieren vorhanden sind
id: $('#matrixprodukt_uebersetzung_id').val(),
sprache_von: $('#matrixprodukt_sprache_von').val(),
name_von: $('#matrixprodukt_name_von').val(),
name_ext_von: $('#matrixprodukt_name_ext_von').val(),
sprache_nach: $('#matrixprodukt_sprache_nach').val(),
name_nach: $('#matrixprodukt_name_nach').val(),
name_ext_nach: $('#matrixprodukt_name_ext_nach').val(),
uebersetzung_projekt: $('#matrixprodukt_uebersetzung_projekt').val(),
aktiv: $('#matrixprodukt_uebersetzung_aktiv').prop("checked")?1:0
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
App.loading.close();
if (data.status == 1) {
MatrixproduktUebersetzungReset();
updateLiveTable();
$("#editMatrixproduktUebersetzung").dialog('close');
} else {
alert(data.statusText);
}
}
});
}
function MatrixproduktUebersetzungEdit(id) {
if(id > 0)
{
$.ajax({
url: 'index.php?module=matrixprodukt&action=list&cmd=uebersetzungedit',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_uebersetzung_id').val(data.id);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_sprache_von').val(data.language_from);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_von').val(data.name_from);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_ext_von').val(data.name_external_from);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_sprache_nach').val(data.language_to);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_nach').val(data.name_to);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_name_ext_nach').val(data.name_external_to);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_uebersetzung_projekt').val(data.project);
$('#editMatrixproduktUebersetzung').find('#matrixprodukt_uebersetzung_aktiv').prop("checked",data.active==1?true:false);
App.loading.close();
$("#editMatrixproduktUebersetzung").dialog('open');
}
});
} else {
MatrixproduktUebersetzungReset();
$("#editMatrixproduktUebersetzung").dialog('open');
}
}
function MatrixproduktUebersetzungDelete(id) {
var conf = confirm('Wirklich löschen?');
if (conf) {
$.ajax({
url: 'index.php?module=matrixprodukt&action=list&cmd=uebersetzungdelete',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
if(data.status == 1){
updateLiveTable();
}else{
alert(data.statusText);
}
App.loading.close();
}
});
}
return false;
}
function MatrixproduktReset(){
$('#editMatrixprodukt').find('#matrixprodukt_id').val('');
$('#editMatrixprodukt').find('#matrixprodukt_name').val('');
$('#editMatrixprodukt').find('#matrixprodukt_name_ext').val('');
$('#editMatrixprodukt').find('#matrixprodukt_projekt').val('');
$('#editMatrixprodukt').find('#matrixprodukt_aktiv').prop("checked", true);
}
function MatrixproduktEditSave() {
$.ajax({
url: 'index.php?module=matrixprodukt&action=list&cmd=save',
data: {
//Alle Felder die fürs editieren vorhanden sind
id: $('#matrixprodukt_id').val(),
name: $('#matrixprodukt_name').val(),
name_ext: $('#matrixprodukt_name_ext').val(),
projekt: $('#matrixprodukt_projekt').val(),
aktiv: $('#matrixprodukt_aktiv').prop("checked")?1:0
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
App.loading.close();
if (data.status == 1) {
MatrixproduktReset();
updateLiveTable();
$("#editMatrixprodukt").dialog('close');
} else {
alert(data.statusText);
}
}
});
}
function MatrixproduktEdit(id) {
if(id > 0)
{
$.ajax({
url: 'index.php?module=matrixprodukt&action=list&cmd=edit',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
$('#editMatrixprodukt').find('#matrixprodukt_id').val(data.id);
$('#editMatrixprodukt').find('#matrixprodukt_name').val(data.name);
$('#editMatrixprodukt').find('#matrixprodukt_name_ext').val(data.name_ext);
$('#editMatrixprodukt').find('#matrixprodukt_projekt').val(data.projekt);
$('#editMatrixprodukt').find('#matrixprodukt_aktiv').prop("checked",data.aktiv==1?true:false);
App.loading.close();
$("#editMatrixprodukt").dialog('open');
}
});
} else {
MatrixproduktReset();
$("#editMatrixprodukt").dialog('open');
}
}
function updateLiveTable(i) {
var oTableL = $('#matrixprodukt_eigenschaftengruppen').dataTable();
var tmp = $('.dataTables_filter input[type=search]').val();
oTableL.fnFilter('%');
//oTableL.fnFilter('');
oTableL.fnFilter(tmp);
}
function MatrixproduktDelete(id) {
var conf = confirm('Wirklich löschen?');
if (conf) {
$.ajax({
url: 'index.php?module=matrixprodukt&action=list&cmd=delete',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
if(data.status == 1){
updateLiveTable();
}else{
alert(data.statusText);
}
App.loading.close();
}
});
}
return false;
}
</script>
<div id="vueapp"></div>

View File

@ -1,12 +1,14 @@
<!-- gehort zu tabview -->
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2019 Xentral (c) Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg, Germany
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<div id="tabs">
<ul>
<li><a href="#tabs-1">[TABTEXT]</a></li>
</ul>
<!-- erstes tab -->
<div id="tabs-1">
<div class="row">
<div class="row-height">
<div class="col-xs-12 col-md-10 col-md-height">
@ -15,384 +17,12 @@
[TAB1]
</div>
</div>
<div class="col-xs-12 col-md-2 col-md-height">
<div class="inside inside-full-height">
<fieldset>
<legend>{|Aktionen|}</legend>
<input type="button" class="btnGreenNew" name="neueoption" value="&#10010; Neuer Eintrag" onclick="MatrixproduktOptionenEdit(0);">
<input type="button" class="btnGreenNew" name="neueuebersetzung" value="&#10010; Neue &Uuml;bersetzung" onclick="MatrixproduktOptionenUebersetzungEdit(0);">
</fieldset>
</div>
</div>
</div>
</div>
</div>
<!-- tab view schließen -->
</div>
<!-- ende tab view schließen -->
<div id="editMatrixproduktOptionen" style="display:none;" title="Bearbeiten">
<form action="" method="post" name="eprooform" >
<input type="hidden" id="matrixprodukt_optionen_id">
<input type="hidden" name = "matrixprodukt_eintragid" id="matrixprodukt_eintragid" value="[ID]">
<fieldset>
<legend>{|Einstellungen|}</legend>
<table>
<tr>
<td width="100">{|Name|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_name" id="matrixprodukt_optionen_name"></td>
</tr>
<tr>
<td width="100">{|Anhang an Artikelnummer|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_articlenumber_suffix" id="matrixprodukt_optionen_articlenumber_suffix"></td>
</tr>
<tr[STYLEEXT]>
<td>{|Name Extern|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_name_ext" id="matrixprodukt_optionen_name_ext"></td>
</tr>
<tr>
<td>{|Sortierung|}:</td>
<td><input type="text" size="8" name="matrixprodukt_optionen_sortierung" id="matrixprodukt_optionen_sortierung"></td>
</tr>
<tr>
<td>{|Aktiv|}:</td>
<td><input type="checkbox" name="matrixprodukt_optionen_aktiv" id="matrixprodukt_optionen_aktiv" value="1"></td>
</tr>
</table>
</fieldset>
</form>
</div>
<div id="editMatrixproduktOptionenUebersetzung" style="display:none;" title="Bearbeiten">
<form action="" method="post" name="eprooform">
<input type="hidden" id="matrixprodukt_optionen_uebersetzung_id">
<input type="hidden" name="matrixprodukt_eintrag_uebersetzung_id" id="matrixprodukt_eintrag_uebersetzung_id" value="[ID]">
<fieldset>
<legend>{|Von|}</legend>
<table>
<tr>
<td width="100">{|Sprache|}:</td>
<td><select name="matrixprodukt_optionen_sprache_von" id="matrixprodukt_optionen_sprache_von">
[SPRACHEN]
</select></td>
</tr>
<tr>
<td>{|Name|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_name_von" id="matrixprodukt_optionen_name_von"></td>
</tr>
<tr>
<td>{|Name Extern|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_name_ext_von" id="matrixprodukt_optionen_name_ext_von"></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>{|Nach|}</legend>
<table>
<tr>
<td width="100">{|Sprache|}:</td>
<td><select name="matrixprodukt_optionen_sprache_nach" id="matrixprodukt_optionen_sprache_nach">
[SPRACHEN]
</select></td>
</tr>
<tr>
<td>{|Name|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_name_nach" id="matrixprodukt_optionen_name_nach">
</tr>
<tr>
<td>{|Name Extern|}:</td>
<td><input type="text" size="40" name="matrixprodukt_optionen_name_ext_nach" id="matrixprodukt_optionen_name_ext_nach"></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>{|Einstellungen|}</legend>
<table>
<tr>
<td width="100">{|Aktiv|}:</td>
<td><input type="checkbox" name="matrixprodukt_optionen_uebersetzung_aktiv" id="matrixprodukt_optionen_uebersetzung_aktiv">
</tr>
</table>
</fieldset>
</form>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#matrixprodukt_optionen_name').focus();
$("#editMatrixproduktOptionen").dialog({
modal: true,
bgiframe: true,
closeOnEscape:false,
minWidth:500,
maxHeight:800,
autoOpen: false,
buttons: {
ABBRECHEN: function() {
MatrixproduktOptionenReset();
$(this).dialog('close');
},
SPEICHERN: function() {
MatrixproduktOptionenEditSave();
}
}
});
$("#editMatrixproduktOptionen").dialog({
close: function( event, ui ) {MatrixproduktOptionenReset();}
});
$("#editMatrixproduktOptionenUebersetzung").dialog({
modal: true,
bgiframe: true,
closeOnEscape:false,
minWidth:500,
maxHeight:800,
autoOpen: false,
buttons: {
ABBRECHEN: function() {
MatrixproduktOptionenUebersetzungReset();
$(this).dialog('close');
},
SPEICHERN: function() {
MatrixproduktOptionenUebersetzungEditSave();
}
}
});
$("#editMatrixproduktOptionenUebersetzung").dialog({
close: function( event, ui ) {MatrixproduktOptionenUebersetzungReset();}
});
});
function MatrixproduktOptionenUebersetzungReset(){
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_uebersetzung_id').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_sprache_von').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_von').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_ext_von').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_sprache_nach').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_nach').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_ext_nach').val('');
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_uebersetzung_aktiv').prop("checked", true);
var languageFrom = document.getElementById('matrixprodukt_optionen_sprache_von');
languageFrom.selectedIndex = 0;
var languageTo = document.getElementById('matrixprodukt_optionen_sprache_nach');
languageTo.selectedIndex = 0;
}
function MatrixproduktOptionenUebersetzungEditSave() {
$.ajax({
url: 'index.php?module=matrixprodukt&action=optionenlist&cmd=optionenuebersetzungsave',
data: {
//Alle Felder die fürs editieren vorhanden sind
optionenid: $('#matrixprodukt_optionen_uebersetzung_id').val(),
matrixproduktid: $('#matrixprodukt_eintrag_uebersetzung_id').val(),
sprachevon: $('#matrixprodukt_optionen_sprache_von').val(),
optionennamevon: $('#matrixprodukt_optionen_name_von').val(),
optionenname_extvon: $('#matrixprodukt_optionen_name_ext_von').val(),
sprachenach: $('#matrixprodukt_optionen_sprache_nach').val(),
optionennamenach: $('#matrixprodukt_optionen_name_nach').val(),
optionenname_extnach: $('#matrixprodukt_optionen_name_ext_nach').val(),
optionenaktiv: $('#matrixprodukt_optionen_uebersetzung_aktiv').prop("checked")?1:0
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
App.loading.close();
if (data.status == 1) {
MatrixproduktOptionenUebersetzungReset();
updateLiveTableOptionen();
$("#editMatrixproduktOptionenUebersetzung").dialog('close');
} else {
alert(data.statusText);
}
}
});
}
function MatrixproduktOptionenUebersetzungEdit(id) {
if(id > 0)
{
$.ajax({
url: 'index.php?module=matrixprodukt&action=optionenlist&cmd=optionenuebersetzungedit',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_uebersetzung_id').val(data.id);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_sprache_von').val(data.language_from);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_von').val(data.name_from);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_ext_von').val(data.name_external_from);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_sprache_nach').val(data.language_to);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_nach').val(data.name_to);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_name_ext_nach').val(data.name_external_to);
$('#editMatrixproduktOptionenUebersetzung').find('#matrixprodukt_optionen_uebersetzung_aktiv').prop("checked",data.active==1?true:false);
App.loading.close();
$("#editMatrixproduktOptionenUebersetzung").dialog('open');
}
});
} else {
MatrixproduktOptionenUebersetzungReset();
$("#editMatrixproduktOptionenUebersetzung").dialog('open');
}
}
function MatrixproduktOptionenUebersetzungDelete(id) {
var conf = confirm('Wirklich löschen?');
if (conf) {
$.ajax({
url: 'index.php?module=matrixprodukt&action=optionenlist&cmd=optionenuebersetzungdelete',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
if(data.status == 1){
updateLiveTableOptionen();
}else{
alert(data.statusText);
}
App.loading.close();
}
});
}
return false;
}
function MatrixproduktOptionenReset(){
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_id').val('');
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_name').val('');
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_name_ext').val('');
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_articlenumber_suffix').val('');
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_sortierung').val('');
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_aktiv').prop("checked", true);
}
function MatrixproduktOptionenEditSave() {
$.ajax({
url: 'index.php?module=matrixprodukt&action=optionenlist&cmd=optionensave',
data: {
//Alle Felder die fürs editieren vorhanden sind
optionenid: $('#matrixprodukt_optionen_id').val(),
matrixproduktid: $('#matrixprodukt_eintragid').val(),
optionenname: $('#matrixprodukt_optionen_name').val(),
optionenarticlenumber_suffix: $('#matrixprodukt_optionen_articlenumber_suffix').val(),
optionenname_ext: $('#matrixprodukt_optionen_name_ext').val(),
optionensortierung: $('#matrixprodukt_optionen_sortierung').val(),
optionenaktiv: $('#matrixprodukt_optionen_aktiv').prop("checked")?1:0
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
App.loading.close();
if (data.status == 1) {
MatrixproduktOptionenReset();
updateLiveTableOptionen();
$("#editMatrixproduktOptionen").dialog('close');
} else {
alert(data.statusText);
}
}
});
}
function MatrixproduktOptionenEdit(id) {
if(id > 0)
{
$.ajax({
url: 'index.php?module=matrixprodukt&action=optionenlist&cmd=optionenedit',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_id').val(data.opt_id);
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_name').val(data.opt_name);
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_articlenumber_suffix').val(data.opt_articlenumber_suffix);
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_name_ext').val(data.opt_name_ext);
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_sortierung').val(data.opt_sortierung);
$('#editMatrixproduktOptionen').find('#matrixprodukt_optionen_aktiv').prop("checked",data.opt_aktiv==1?true:false);
App.loading.close();
$("#editMatrixproduktOptionen").dialog('open');
}
});
} else {
MatrixproduktOptionenReset();
$("#editMatrixproduktOptionen").dialog('open');
}
}
function updateLiveTableOptionen(i) {
var oTableL = $('#matrixprodukt_eigenschaftenoptionen').dataTable();
var tmp = $('.dataTables_filter input[type=search]').val();
oTableL.fnFilter('%');
//oTableL.fnFilter('');
oTableL.fnFilter(tmp);
}
function MatrixproduktOptionenDelete(id) {
var conf = confirm('Wirklich löschen?');
if (conf) {
$.ajax({
url: 'index.php?module=matrixprodukt&action=optionenlist&cmd=optionendelete',
data: {
id: id
},
method: 'post',
dataType: 'json',
beforeSend: function() {
App.loading.open();
},
success: function(data) {
if(data.status == 1){
updateLiveTableOptionen();
}else{
alert(data.statusText);
}
App.loading.close();
}
});
}
return false;
}
</script>
<div id="vueapp"></div>

View File

@ -0,0 +1,55 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2019 Xentral (c) Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg, Germany
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<div id="tabs">
<ul>
<li><a href="#tabs-1">Gruppen</a></li>
<li><a href="#tabs-2">Optionen</a></li>
</ul>
<!-- erstes tab -->
<div id="tabs-1">
<div class="row">
<div class="row-height">
<div class="col-xs-12 col-md-10 col-md-height">
<div class="inside-white inside-full-height">
[TABGRP]
</div>
</div>
<div class="col-xs-12 col-md-2 col-md-height">
<div class="inside inside-full-height">
<fieldset>
<legend>{|Aktionen|}</legend>
<input type="button" class="btnGreenNew vueAction" value="&#10010; Neue &Uuml;bersetzung" data-action="translationEdit" data-type="group">
</fieldset>
</div>
</div>
</div>
</div>
</div>
<!-- erstes tab -->
<div id="tabs-2">
<div class="row">
<div class="row-height">
<div class="col-xs-12 col-md-10 col-md-height">
<div class="inside-white inside-full-height">
[TABOPT]
</div>
</div>
<div class="col-xs-12 col-md-2 col-md-height">
<div class="inside inside-full-height">
<fieldset>
<legend>{|Aktionen|}</legend>
<input type="button" class="btnGreenNew vueAction" value="&#10010; Neue &Uuml;bersetzung" data-action="translationEdit" data-type="option">
</fieldset>
</div>
</div>
</div>
</div>
</div>
<!-- tab view schließen -->
</div>
<!-- ende tab view schließen -->
<div id="vueapp"></div>

448
www/pages/matrixprodukt.php Normal file
View File

@ -0,0 +1,448 @@
<?php
/*
* SPDX-FileCopyrightText: 2023 Andreas Palm
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
use Xentral\Components\Http\JsonResponse;
use Xentral\Components\Http\Request;
use Xentral\Components\Http\Response;
use Xentral\Modules\Article\Service\ArticleService;
use Xentral\Modules\MatrixProduct\Data\Group;
use Xentral\Modules\MatrixProduct\Data\Option;
use Xentral\Modules\MatrixProduct\Data\Translation;
use Xentral\Modules\MatrixProduct\MatrixProductService;
class Matrixprodukt
{
private ApplicationCore $app;
private MatrixProductService $service;
private ArticleService $articleService;
private Request $request;
const MODULE_NAME = 'MatrixProduct';
public function __construct(ApplicationCore $app, bool $intern = false)
{
$this->app = $app;
if ($intern)
return;
if (!$this->app instanceof Application)
return;
$this->service = $this->app->Container->get('MatrixProductService');
$this->request = $this->app->Container->get('Request');
$this->articleService = $this->app->Container->get('ArticleService');
$this->app->ActionHandlerInit($this);
$this->app->ActionHandler("list", "ActionList");
$this->app->ActionHandler("optionenlist", "ActionOptionList");
$this->app->ActionHandler("artikel", "ActionArticle");
$this->app->ActionHandler("translation", "ActionTranslation");
$this->app->ActionHandlerListen($app);
}
private function createMenu(): void
{
$this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=list", "&Uuml;bersicht");
$this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=translation", "&Uuml;bersetzungen");
}
public function Install()
{
}
public function TableSearch(&$app, $name, $erlaubtevars)
{
switch ($name) {
case "matrixprodukt_groups":
$allowed['matrixprodukt_list'] = array('list');
$heading = array('', 'Name', 'Name (extern)', 'Men&uuml;');
$width = array('1%', '30%', '30%', '1%'); // Fill out manually later
$findcols = array('mg.id', 'mg.name', 'mg.name_ext');
$searchsql = array('mg.name', 'mg.name_ext');
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"groupEdit\" data-group-id=\"%value%\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<a href=\"index.php?module=matrixprodukt&action=optionenlist&id=%value%\">"
. "<img src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/forward.svg\"></a>&nbsp;"
. "<img class=\"vueAction\" data-action=\"groupDelete\" data-group-id=\"%value%\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sql = "SELECT SQL_CALC_FOUND_ROWS mg.id, mg.id, mg.name, mg.name_ext, mg.id FROM matrixprodukt_eigenschaftengruppen mg";
$where = "1";
$count = "SELECT count(DISTINCT id) FROM matrixprodukt_eigenschaftengruppen WHERE $where";
break;
case "matrixprodukt_options":
$id = $this->app->Secure->GetGET('id');
$heading = array('', 'Name', 'Name (extern)', 'Men&uuml;');
$width = array('1%', '30%', '30%', '1%');
$findcols = array('mo.id', 'mo.name', 'mo.name_ext');
$searchsql = array('mo.name', 'mo.name_ext');
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"optionEdit\" data-option-id=\"%value%\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<img class=\"vueAction\" data-action=\"optionDelete\" data-option-id=\"%value%\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sql = "SELECT SQL_CALC_FOUND_ROWS mo.id, mo.id, mo.name, mo.name_ext, mo.id FROM matrixprodukt_eigenschaftenoptionen mo";
$where = "mo.gruppe = $id";
$count = "SELECT count(DISTINCT mo.id) FROM matrixprodukt_eigenschaftenoptionen mo WHERE $where";
break;
case "matrixprodukt_article_groups":
$id = $this->app->Secure->GetGET('id');
$heading = array('', 'Name', 'Name (extern)', 'Men&uuml;');
$width = array('1%', '30%', '30%', '1%'); // Fill out manually later
$findcols = array('mga.id', 'mga.name', 'mga.name_ext');
$searchsql = array('mga.name', 'mga.name_ext');
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"groupEdit\" data-group-id=\"%value%\" data-article-id=\"$id\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<a href=\"index.php?module=matrixprodukt&action=artikel&id=$id&sid=%value%\">"
. "<img src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/forward.svg\"></a>&nbsp;"
. "<img class=\"vueAction\" data-action=\"groupDelete\" data-group-id=\"%value%\" data-article-id=\"$id\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sql = "SELECT SQL_CALC_FOUND_ROWS mga.id, mga.id, mga.name, mga.name_ext, mga.id FROM matrixprodukt_eigenschaftengruppen_artikel mga";
$where = "mga.artikel = $id";
$count = "SELECT count(DISTINCT mga.id) FROM matrixprodukt_eigenschaftengruppen_artikel mga WHERE $where";
break;
case "matrixprodukt_article_options":
$id = $this->app->Secure->GetGET('id');
$groupId = $this->app->Secure->GetGET('sid');
$heading = array('', 'Name', 'Name (extern)', 'Men&uuml;');
$width = array('1%', '30%', '30%', '1%');
$findcols = array('moa.id', 'moa.name', 'moa.name_ext');
$searchsql = array('moa.name', 'moa.name_ext');
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"optionEdit\" data-option-id=\"%value%\" data-article-id=\"$id\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<img class=\"vueAction\" data-action=\"optionDelete\" data-option-id=\"%value%\" data-article-id=\"$id\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sql = "SELECT SQL_CALC_FOUND_ROWS moa.id, moa.id, moa.name, moa.name_ext, moa.id FROM matrixprodukt_eigenschaftenoptionen_artikel moa";
$where = "moa.gruppe = $groupId";
$count = "SELECT count(DISTINCT moa.id) FROM matrixprodukt_eigenschaftenoptionen_artikel moa WHERE $where";
break;
case "matrixprodukt_variants":
$id = $this->app->Secure->GetGET('id');
$groups = $this->app->DB->SelectPairs("SELECT id, name FROM matrixprodukt_eigenschaftengruppen_artikel WHERE artikel = $id");
$heading[] = 'Artikel';
$width[] = '5%';
$nameColumns = [];
$joins = [];
foreach ($groups as $groupId => $groupName) {
$heading[] = $groupName;
$width[] = '5%';
$nameColumns[] = "MAX(moa_$groupId.name)";
$joins[] = "LEFT JOIN matrixprodukt_eigenschaftenoptionen_artikel moa_$groupId
ON mota.option_id=moa_$groupId.id AND moa_$groupId.gruppe = $groupId";
}
$heading[] = 'Men&uuml;';
$width[] = '1%';
$findcols = array_merge($nameColumns, ['nummer', 'id']);
$searchsql = $nameColumns;
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"variantEdit\" data-variant-id=\"%value%\" data-article-id=\"$id\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<img class=\"vueAction\" data-action=\"variantDelete\" data-variant-id=\"%value%\" data-article-id=\"$id\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sqlNameCols = join(',', array_merge(['art.id', 'art.nummer'], $nameColumns, ['art.id']));
$joinSql = join("\n", $joins);
$sql = "SELECT SQL_CALC_FOUND_ROWS $sqlNameCols
FROM artikel art
LEFT JOIN matrixprodukt_optionen_zu_artikel mota ON mota.artikel = art.id
$joinSql";
$where = "art.variante_von = $id";
$groupby = "GROUP BY art.id";
$count = "SELECT count(*) FROM artikel art WHERE $where";
break;
case "matrixproduct_group_translations":
$heading = array('Name', 'Name (extern)', 'Sprache', 'Übersetzung Name', 'Übersetzung Name (extern)', 'Men&uuml;');
$width = array('20%', '20%', '5%', '20%', '20%', '1%');
$findcols = array('mat.name_from', 'mat.name_external_from', 'mat.name_to', 'mat.name_external_to');
$searchsql = array('mat.name_from', 'mat.name_external_from');
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"translationEdit\" data-type=\"group\" data-id=\"%value%\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<img class=\"vueAction\" data-action=\"translationDelete\" data-type=\"group\" data-id=\"%value%\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sql = "SELECT SQL_CALC_FOUND_ROWS mat.id, mat.name_from, mat.name_external_from, mat.language_to, mat.name_to, mat.name_external_to, mat.id FROM matrix_article_translation mat";
$where = "1";
$count = "SELECT count(DISTINCT mat.id) FROM matrix_article_translation mat WHERE $where";
break;
case "matrixproduct_option_translations":
$heading = array('Name', 'Name (extern)', 'Sprache', 'Übersetzung Name', 'Übersetzung Name (extern)', 'Men&uuml;');
$width = array('20%', '20%', '5%', '20%', '20%', '1%');
$findcols = array('mat.name_from', 'mat.name_external_from', 'mat.name_to', 'mat.name_external_to');
$searchsql = array('mat.name_from', 'mat.name_external_from');
$menu = "<table><tr><td nowrap>"
. "<img class=\"vueAction\" data-action=\"translationEdit\" data-type=\"option\" data-id=\"%value%\" src=\"./themes/{$app->Conf->WFconf['defaulttheme']}/images/edit.svg\">&nbsp;"
. "<img class=\"vueAction\" data-action=\"translationDelete\" data-type=\"option\" data-id=\"%value%\" src=\"themes/{$app->Conf->WFconf['defaulttheme']}/images/delete.svg\">"
. "</td></tr></table>";
$sql = "SELECT SQL_CALC_FOUND_ROWS mat.id, mat.name_from, mat.name_external_from, mat.language_to, mat.name_to, mat.name_external_to, mat.id FROM matrix_article_options_translation mat";
$where = "1";
$count = "SELECT count(DISTINCT mat.id) FROM matrix_article_options_translation mat WHERE $where";
break;
}
$erg = false;
foreach ($erlaubtevars as $k => $v) {
if (isset($$v)) {
$erg[$v] = $$v;
}
}
return $erg;
}
public function ActionList()
{
$cmd = $this->app->Secure->GetGET('cmd');
switch ($cmd) {
case "edit":
$id = intval($this->app->Secure->GetGET('groupId'));
$group = $this->service->GetGlobalGroupById($id);
if (!$group)
return JsonResponse::NotFound();
$group->project = $this->app->DB->SelectRow("SELECT id, abkuerzung, name FROM projekt WHERE id = $group->projectId");
return new JsonResponse($group);
case "save":
$json = $this->request->getJson();
$group = new Group($json->name, $json->id ?? null, $json->active ?? false, $json->nameExternal ?? '', $json->project->id ?? 0, $json->required ?? false);
$this->service->SaveGlobalGroup($group);
return JsonResponse::NoContent();
case "delete":
$json = $this->request->getJson();
$this->service->DeleteGlobalGroup($json->groupId);
return JsonResponse::NoContent();
case "selectoptions":
$result = [];
$sql = "SELECT mg.id groupid, mg.name groupname, mo.id optionid, mo.name optionname FROM matrixprodukt_eigenschaftengruppen mg JOIN matrixprodukt_eigenschaftenoptionen mo ON mo.gruppe = mg.id WHERE mg.aktiv = 1 AND mo.aktiv = 1";
foreach ($this->app->DB->SelectArr($sql) as $row) {
$groupid = $row['groupid'];
if (!in_array($groupid, $result)) {
$result[$groupid]['id'] = $groupid;
$result[$groupid]['name'] = $row['groupname'];
}
$result[$groupid]['options'][] = ['id' => $row['optionid'], 'name' => $row['optionname']];
}
return new JsonResponse(array_values($result));
}
$this->createMenu();
$this->app->Tpl->Set('TABSADD', '<input type="button" class="neubutton vueAction" value="NEU" data-action="groupEdit">');
$this->app->YUI->TableSearch('TAB1', 'matrixprodukt_groups', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->Tpl->Parse('PAGE', "matrixprodukt_list.tpl");
}
public function ActionOptionList()
{
$this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=optionenlist", "Optionen");
$id = $this->request->get->getInt('id');
$cmd = $this->app->Secure->GetGET('cmd');
switch ($cmd) {
case "edit":
$id = intval($this->app->Secure->GetGET('optionId'));
$option = $this->service->GetGlobalOptionById($id);
if (!$option)
return JsonResponse::NotFound();
return new JsonResponse($option);
case "save":
$json = $this->request->getJson();
$option = new Option($json->name, $json->groupId, $json->id, $json->active ?? false,
$json->nameExternal ?? '', $json->sort ?? 0, $json->articleNumber ?? '',
$json->articleNumberSuffix ?? '');
$this->service->SaveGlobalOption($option);
return JsonResponse::NoContent();
case "delete":
$json = $this->request->getJson();
$this->service->DeleteGlobalOption($json->optionId);
return JsonResponse::NoContent();
}
$group = $this->service->GetGlobalGroupById($id);
$this->app->Tpl->Set('KURZUEBERSCHRIFT1', $group->name);
$this->app->Tpl->Set('TABSADD', '<input type="button" class="neubutton vueAction" value="NEU" data-action="optionEdit" data-group-id="' . $id . '">');
$this->app->YUI->TableSearch('TAB1', 'matrixprodukt_options', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->Tpl->Parse('PAGE', "matrixprodukt_optionen_list.tpl");
}
public function ActionArticle()
{
$cmd = $this->app->Secure->GetGET('cmd');
$articleModule = $this->app->erp->LoadModul('artikel');
$articleModule?->ArtikelMenu();
switch ($cmd) {
case "addoptions":
$json = $this->request->getJson();
$this->service->AddGlobalOptionsForArticle($json->articleId, $json->optionIds);
return JsonResponse::NoContent();
case "groupedit":
$groupId = intval($this->app->Secure->GetGET('groupId'));
if (!$groupId)
return JsonResponse::NotFound();
$group = $this->service->GetArticleGroupById($groupId);
$group->project = $this->app->DB->SelectRow("SELECT id, abkuerzung, name FROM projekt WHERE id = $group->projectId");
return new JsonResponse($group);
case "groupsave":
$json = $this->request->getJson();
$group = new Group($json->name, $json->groupId, $json->active ?? false, $json->nameExternal ?? '',
$json->project->id ?? 0, $json->required ?? false, $json->articleId, $json->sort ?? 0);
$this->service->SaveArticleGroup($group);
return JsonResponse::NoContent();
case "groupdelete":
$json = $this->request->getJson();
if (!$this->service->DeleteArticleGroup($json->groupId))
return JsonResponse::BadRequest(['error' => 'Die Gruppe wird noch von Variantenartikeln verwendet.']);
return JsonResponse::NoContent();
case "optionedit":
$optionId = intval($this->app->Secure->GetGET('optionId'));
$option = $this->service->GetArticleOptionById($optionId);
if (!$option)
return JsonResponse::NotFound();
return new JsonResponse($option);
case "optionsave":
$json = $this->request->getJson();
$option = new Option($json->name, $json->groupId, $json->optionId, $json->active ?? false,
$json->nameExternal ?? '', $json->sort ?? 0, '',
$json->articleNumberSuffix ?? '', 0, $json->articleId);
$this->service->SaveArticleOption($option);
return JsonResponse::NoContent();
case "optiondelete":
$json = $this->request->getJson();
if (!$this->service->DeleteArticleOption($json->optionId))
return JsonResponse::BadRequest(['error' => 'Die Option wird noch von Variantenartikeln verwendet.']);
return JsonResponse::NoContent();
case "variantedit":
$articleId = $this->request->get->getInt('articleId');
$variantId = $this->request->get->getInt('variantId');
$groups = $this->service->GetArticleGroupsByArticleId($articleId);
$options = $this->service->GetArticleOptionsByArticleId($articleId);
$selected = $this->service->GetSelectedOptionIdsByVariantId($variantId);
$result = [];
foreach ($groups as $group) {
$result[$group->id] = [
'name' => $group->name,
'selected' => 0,
'options' => []
];
}
foreach ($options as $option) {
$result[$option->groupId]['options'][] = ['value' => $option->id, 'name' => $option->name];
if (in_array($option->id, $selected))
$result[$option->groupId]['selected'] = $option->id;
}
$variant = $this->app->DB->SelectRow("SELECT id, nummer, name_de FROM artikel WHERE id = $variantId");
return new JsonResponse([
'groups' => $result,
'variant' => $variant
]);
case "variantsave":
$json = $this->request->getJson();
$optionIds = [];
foreach ($json->groups as $group) {
if ($group->selected > 0)
$optionIds[] = intval($group->selected);
}
if (empty($optionIds))
return JsonResponse::BadRequest();
$res = $this->service->SaveVariant($json->articleId, $json->variant->id, $optionIds, $json->variantId);
if ($res === true)
return JsonResponse::NoContent();
return new JsonResponse([$res], Response::HTTP_BAD_REQUEST);
case "variantdelete":
$json = $this->request->getJson();
$this->service->DeleteVariant($json->variantId);
return JsonResponse::NoContent();
case "createMissing":
if ($this->request->getMethod() === 'GET') {
$articleId = $this->request->get->getInt('articleId');
$groups = $this->service->GetArticleGroupsByArticleId($articleId);
$options = $this->service->GetArticleOptionsByArticleId($articleId);
foreach ($groups as $group) {
$result[$group->id] = [
'name' => $group->name,
'selected' => [],
'required' => $group->required,
'options' => []
];
}
foreach ($options as $option) {
$result[$option->groupId]['options'][] = ['value' => $option->id, 'name' => $option->name];
$result[$option->groupId]['selected'][] = $option->id;
}
return new JsonResponse(['groups' => $result ?? []]);
} else {
$json = $this->request->getJson();
$list = [[]];
foreach ($json->groups as $group) {
if (empty($group->selected))
continue;
$newList = [];
foreach ($list as $old) {
foreach ($group->selected as $option) {
$newList[] = array_merge($old, [$option]);
}
}
$list = $newList;
}
$oldnumber = $this->app->DB->Select("SELECT nummer FROM artikel WHERE id = $json->articleId");
$created = [];
foreach ($list as $optionSet) {
$variantId = $this->service->GetVariantIdByOptionSet($optionSet);
if ($variantId)
continue;
$number = $oldnumber.'_'.$this->service->GetSuffixStringForOptionSet($optionSet);
$newId = $this->articleService->CopyArticle($json->articleId, true, true, true, true, true, true, true, $number);
$this->service->SaveVariant($json->articleId, $newId, $optionSet);
$created[] = $number;
}
return new JsonResponse($created);
}
}
$articleId = $this->app->Secure->GetGET('id');
$groupId = $this->app->Secure->GetGET('sid');
if (empty($groupId)) {
$this->app->YUI->TableSearch('TAB1', 'matrixprodukt_article_groups', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->Tpl->Set('ADDEDITFUNCTION', 'groupEdit');
} else {
$this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=artikel&id=$articleId", 'Zur&uuml;ck zur Gruppen&uuml;bersicht');
$this->app->YUI->TableSearch('TAB1', 'matrixprodukt_article_options', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->Tpl->Set('SID', $groupId);
$this->app->Tpl->Set('ADDEDITFUNCTION', 'optionEdit');
}
if ($this->service->GetArticleGroupsByArticleId($articleId)) {
$this->app->YUI->TableSearch('TAB2', 'matrixprodukt_variants', "show", "", "", basename(__FILE__), __CLASS__);
} else {
$this->app->Tpl->Set('TAB2HIDEACTIONS', 'style="display: none;"');
$this->app->Tpl->Set('MESSAGE2', "<div class=\"error\">Es sind noch keine Gruppen angelegt!</div>");
}
$this->app->Tpl->Parse('PAGE', "matrixprodukt_article.tpl");
}
public function ActionTranslation() {
$cmd = $this->app->Secure->GetGET('cmd');
switch ($cmd) {
case "edit":
$id = $this->app->Secure->GetGET('id');
$isOption = $this->app->Secure->GetGET('type') === 'option';
$translation = $isOption ? $this->service->GetOptionTranslation($id) : $this->service->GetGroupTranslation($id);
return new JsonResponse($translation);
case "save":
$json = $this->request->getJson();
$isOption = $json->type === 'option';
$translation = new Translation($json->nameFrom, $json->languageTo, $json->nameTo, $json->id,
$json->nameExternalFrom ?? '', $json->nameExternalTo ?? '');
if ($isOption)
$this->service->SaveOptionTranslation($translation);
else
$this->service->SaveGroupTranslation($translation);
return JsonResponse::NoContent();
case "delete":
$json = $this->request->getJson();
$isOption = $json->type === 'option';
if ($isOption)
$this->service->DeleteOptionTranslation($json->id);
else
$this->service->DeleteGroupTranslation($json->id);
return JsonResponse::NoContent();
}
$this->createMenu();
$this->app->YUI->TableSearch('TABGRP', 'matrixproduct_group_translations', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->YUI->TableSearch('TABOPT', 'matrixproduct_option_translations', "show", "", "", basename(__FILE__), __CLASS__);
$this->app->Tpl->Parse('PAGE', "matrixprodukt_translation.tpl");
}
}

View File

@ -6,12 +6,15 @@
* SPDX-License-Identifier: LicenseRef-EGPL-3.1
*/
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
class Shopimporter_Presta extends ShopimporterBase
{
private $app;
private $intern;
private $shopid;
private $data;
var $data;
private $protocol;
private $apiKey;
private $shopUrl;
@ -161,18 +164,21 @@ class Shopimporter_Presta extends ShopimporterBase
public function ImportUpdateAuftrag()
{
$auftrag = $this->data['auftrag'];
/** @var OrderStatusUpdateRequest $data */
$data = $this->CatchRemoteCommand('data');
if ($data->orderStatus !== OrderStatus::Completed)
return;
$obj = $this->prestaRequest('GET', 'order_histories?schema=blank');
$obj->order_history->id_order = $auftrag;
$obj->order_history->id_order = $data->shopOrderId;
$obj->order_history->id_order_state = $this->idabgeschlossen;
$this->prestaRequest('POST', 'order_histories', $obj->asXML());
$req = $this->prestaRequest('GET', "order_carriers?filter[id_order]=$auftrag&display=[id]");
$req = $this->prestaRequest('GET', "order_carriers?filter[id_order]=$data->shopOrderId&display=[id]");
$orderCarrierId = strval($req->order_carriers->order_carrier[0]->id);
$req = $this->prestaRequest('GET', "order_carriers/$orderCarrierId");
$req->order_carrier->tracking_number = $this->data['tracking'];
$req->order_carrier->tracking_number = join(',', $data->getTrackingNumberList());
$this->prestaRequest('PUT', "order_carriers/$orderCarrierId", $req->asXML());
}

View File

@ -14,6 +14,8 @@
?>
<?php
use Xentral\Components\Http\JsonResponse;
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
include_once 'Shopimporter_Shopify_Adapter.php';
@ -3381,17 +3383,21 @@ class Shopimporter_Shopify extends ShopimporterBase
public function ImportUpdateAuftrag()
{
$tmp = $this->CatchRemoteCommand('data');
/** @var OrderStatusUpdateRequest $req */
$req = $this->CatchRemoteCommand('data');
if ($req->orderStatus !== OrderStatus::Completed)
return;
// pruefe ob $tmp[datei] vorhanden wenn nicht lege an sonst update [inhalt] und [checksum]
$auftrag = $tmp['auftrag'];
$auftrag = $req->shopOrderId;
if(!empty($auftrag)){
$zahlungok = $tmp['zahlung'];
$versandok = $tmp['versand'];
$tracking = $tmp['tracking'];
$versandart = $tmp['versandart'];
$data = array();
$data['fulfillment'] = array('tracking_number' => $tracking, 'tracking_company' => $versandart, 'notify_customer' => false);
$data['fulfillment'] = [
'tracking_numbers' => $req->getTrackingNumberList(),
'tracking_company' => $req->shipments[0]?->shippingMethod,
'notify_customer' => false,
'tracking_urls' => $req->getTrackingUrlList()
];
if(!empty($this->location)){
$data['fulfillment']['location_id'] = $this->location;
}
@ -3410,16 +3416,10 @@ class Shopimporter_Shopify extends ShopimporterBase
if($this->shopifytracking){
$data['fulfillment']['notify_customer'] = true;
}
if(!empty($tmp['trackinglinkraw'])) {
$data['fulfillment']['tracking_urls'] = [$tmp['trackinglinkraw']];
}
elseif(!empty($tmp['trackinglink'])){
$data['fulfillment']['tracking_urls'] = [$tmp['trackinglink']];
}
$result = $this->adapter->call('orders/' . $auftrag . '/fulfillments.json', 'POST', $data);
if($this->logging){
$this->app->erp->LogFile(array($tmp, $auftrag, $data, $result['data']));
$this->app->erp->LogFile(array($data, $auftrag, $data, $result['data']));
}
$this->adapter->call('orders/' . $auftrag . '/metafields.json', 'POST', array('metafield' => [
'key' => 'sync_status',
@ -3429,7 +3429,7 @@ class Shopimporter_Shopify extends ShopimporterBase
]));
}else{
if($this->logging){
$this->app->erp->LogFile(array($tmp, $auftrag,'Kein Auftrag'));
$this->app->erp->LogFile(array($data, $auftrag,'Kein Auftrag'));
}
}
return 'OK';

View File

@ -1,4 +1,4 @@
<?php
<?php
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*
@ -10,10 +10,12 @@
* to obtain the text of the corresponding license version.
*
**** END OF COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*/
?>
*/
?>
<?php
use Xentral\Components\Http\JsonResponse;
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
class Shopimporter_Shopware extends ShopimporterBase
{
@ -2520,38 +2522,19 @@ class Shopimporter_Shopware extends ShopimporterBase
//TODO fuer AuftragImport
public function ImportUpdateAuftrag()
{
$tmp = $this->CatchRemoteCommand('data');
/** @var OrderStatusUpdateRequest $data */
$data = $this->CatchRemoteCommand('data');
if ($data->orderStatus !== OrderStatus::Completed)
return;
// pruefe ob $tmp[datei] vorhanden wenn nicht lege an sonst update [inhalt] und [checksum]
$auftrag = $tmp['auftrag'];
$zahlungok = $tmp['zahlung'];
$versandok = $tmp['versand'];
$tracking = $tmp['tracking'];
$auftrag = $data->shopOrderId;
/*if($zahlungok=='ok' || $zahlungok=='1')
$status_zahlung=12;
else
$status_zahlung=1;
if($versandok=='ok' || $versandok=='1')
$status_versand=7;
else
$status_versand=1;*/
/*
$date = new DateTime();
$date->modify('+10 days');
$date = $date->format(DateTime::ISO8601);
*/
$result = $this->adapter->put('orders/'.$auftrag, array(
// 'paymentStatusId' => $status_zahlung,
'orderStatusId' => $this->abgeschlossenStatusId,//$status_versand,
'trackingCode' => $tracking
//'comment' => 'Neuer Kommentar',
//'transactionId' => '0',
// 'clearedDate' => $date,
'orderStatusId' => $this->abgeschlossenStatusId,
'trackingCode' => join(',', $data->getTrackingNumberList())
));
$this->ShopwareLog("Abschlussstatusrückmeldung für Auftrag: $auftrag", print_r($result,true));
//$this->app->DB->Delete("DELETE FROM auftraege WHERE id='$auftrag' LIMIT 1");
return 'ok';
}

View File

@ -15,6 +15,8 @@
<?php
use Xentral\Components\Http\JsonResponse;
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
use Xentral\Modules\Shopware6\Client\Shopware6Client;
use Xentral\Modules\Shopware6\Data\PriceData;
@ -3372,6 +3374,7 @@ class Shopimporter_Shopware6 extends ShopimporterBase
$productPriceType => $lineItem['attributes']['price']['unitPrice'],
'steuersatz' => $lineItem['attributes']['price']['calculatedTaxes'][0]['taxRate'],
];
$this->parseBogxData($lineItem, $product);
$cart['articlelist'][] = $product;
}
@ -3408,9 +3411,12 @@ class Shopimporter_Shopware6 extends ShopimporterBase
*/
public function ImportUpdateAuftrag()
{
$tmp = $this->CatchRemoteCommand('data');
$auftrag = $tmp['auftrag'];
$tracking = $tmp['tracking'];
/** @var OrderStatusUpdateRequest $data */
$data = $this->CatchRemoteCommand('data');
if ($data->orderStatus !== OrderStatus::Completed)
return;
$auftrag = $data->shopOrderId;
$this->shopwareRequest('POST', '_action/order/'.$auftrag.'/state/complete');
@ -3421,17 +3427,17 @@ class Shopimporter_Shopware6 extends ShopimporterBase
$this->shopwareRequest('POST', '_action/order_delivery/'.$deliveryId.'/state/ship');
$deliveryData = [
'trackingCodes' => [$tracking]
'trackingCodes' => [$data->getTrackingNumberList()]
];
$this->shopwareRequest('PATCH', 'order-delivery/'.$deliveryId,$deliveryData);
}
$this->sendInvoce($auftrag);
$this->addCustomFieldToOrder((string)$auftrag);
if(empty($tmp['orderId'])) {
$this->addCustomFieldToOrder($auftrag);
if(empty($data->orderId)) {
return;
}
$this->updateStorageForOrderIntId((int)$tmp['orderId']);
$this->updateStorageForOrderIntId($data->orderId);
}
public function ImportStorniereAuftrag()
@ -3840,4 +3846,48 @@ class Shopimporter_Shopware6 extends ShopimporterBase
$this->updateArticleCacheToSync($articleIds);
}
protected function parseBogxData(array $lineItem, array &$product) : void
{
if (!isset($lineItem['attributes']['payload']['bogxProductConfigurator']))
return;
$bogxdata = $lineItem['attributes']['payload']['bogxProductConfigurator'];
$textlines = [];
if (isset($bogxdata['ordercode']))
$textlines[] = "Order-Code: ${bogxdata['ordercode']}";
foreach ($bogxdata['optionsGroups'] as $bogxposition) {
$dt = $bogxposition['datatype'];
if ($dt == 'quantity_total')
continue;
if (is_array($bogxposition['valueID']) && is_array($bogxposition['title']))
{
foreach ($bogxposition['valueID'] as $valueID) {
$bogxTitle = $bogxposition['title'][$valueID];
if ($dt == 'checkbox_quantity')
$bogxTitle = $bogxposition['label'][$valueID]." ".$bogxTitle;
$textlines[] = sprintf("%s: %s", $bogxposition['groupname'], $bogxTitle);
}
}
else
{
if (is_array($bogxposition['title']))
$bogxTitle = join(' ', $bogxposition['title']);
else
$bogxTitle = $bogxposition['title'];
$textlines[] = sprintf("%s: %s", $bogxposition['groupname'], $bogxTitle);
}
}
if (!empty($bogxdata['shippingtime'])) {
$textlines[] = $bogxdata['shippingtime'];
}
$product['options'] .= join("\n", $textlines);
$product['price'] = $bogxdata['unitySurcharge'];
$product['price_netto'] = $bogxdata['unitySurchargeNetto'];
$product['quantity'] = $bogxdata['totalQuantity'];
}
}

View File

@ -1,4 +1,4 @@
<?php
<?php
/*
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*
@ -10,10 +10,12 @@
* to obtain the text of the corresponding license version.
*
**** END OF COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
*/
?>
*/
?>
<?php
use Xentral\Components\Http\JsonResponse;
use Xentral\Modules\Onlineshop\Data\OrderStatus;
use Xentral\Modules\Onlineshop\Data\OrderStatusUpdateRequest;
class Shopimporter_Woocommerce extends ShopimporterBase
{
@ -442,47 +444,35 @@ class Shopimporter_Woocommerce extends ShopimporterBase
*/
public function ImportUpdateAuftrag()
{
$tmp = $this->CatchRemoteCommand('data');
/** @var OrderStatusUpdateRequest $data */
$data = $this->CatchRemoteCommand('data');
if ($data->orderStatus !== OrderStatus::Completed)
return;
$orderId = $tmp['auftrag'];
$paymentOk = $tmp['zahlung'];
$shippingOk = $tmp['versand'];
$trackingCode = $tmp['tracking'];
$carrier = $tmp['versandart'];
if ($paymentOk === 'ok' || $paymentOk === '1'){
$paymentOk = true;
}
if ($shippingOk === 'ok' || $shippingOk === '1'){
$shippingOk = true;
}
$trackingCode = $data->shipments[0]?->trackingNumber;
if (!empty($trackingCode)) {
$this->client->post('orders/'.$orderId.'/notes', [
$this->client->post('orders/'.$data->orderId.'/notes', [
'note' => 'Tracking Code: ' . $trackingCode
]);
$this->WooCommerceLog("Tracking Code Rückmeldung für Auftrag: $orderId", $trackingCode);
$this->WooCommerceLog("Tracking Code Rückmeldung für Auftrag: $data->orderId", $trackingCode);
}
if ($paymentOk && $shippingOk) {
$updateData = [
'status' => $this->statusCompleted,
'meta_data' => [
[
'key' => 'tracking_code',
'value' => $trackingCode
],
[
'key' => 'shipping_carrier',
'value' => $carrier
]
$updateData = [
'status' => $this->statusCompleted,
'meta_data' => [
[
'key' => 'tracking_code',
'value' => $data->shipments[0]?->trackingNumber
],
];
$this->client->put('orders/'.$orderId, $updateData);
$this->WooCommerceLog("Statusrückmeldung 'completed' für Auftrag: $orderId",$this->statusCompleted );
}
[
'key' => 'shipping_carrier',
'value' => $data->shipments[0]?->shippingMethod
]
],
];
$this->client->put('orders/'.$data->orderId, $updateData);
$this->WooCommerceLog("Statusrückmeldung 'completed' für Auftrag: $data->orderId", $this->statusCompleted );
return 'ok';
}

View File

@ -158,30 +158,15 @@ class Verkaufszahlen {
}else{
$this->app->Tpl->Set('BELEGTYP', 'Auftr&auml;ge');
}
$pkgsubwhere = "DATE(v.datum)=CURDATE()";
if($subwherea)
{
$pakete = $this->getPackages(
" v.versendet_am=DATE_FORMAT(NOW(),'%Y-%m-%d')
AND l.projekt in (".implode(', ', $subwherea).") ".$this->app->erp->ProjektRechte('l.projekt')
);
$this->app->Tpl->Set(
'PAKETE',
$pakete
//$this->app->DB->Select("SELECT COUNT(v.id) FROM versand v INNER JOIN lieferschein l ON v.lieferschein = l.id WHERE v.versendet_am=DATE_FORMAT(NOW(),'%Y-%m-%d') AND l.projekt in (".implode(', ', $subwherea).") ".$this->app->erp->ProjektRechte('l.projekt')."")
);
}else{
$pakete = $this->getPackages(
" v.versendet_am=DATE_FORMAT(NOW(),'%Y-%m-%d')
".$this->app->erp->ProjektRechte('l.projekt')
);
$this->app->Tpl->Set(
'PAKETE',
$pakete
//$this->app->DB->Select("SELECT COUNT(v.id) FROM versand INNER JOIN lieferschein l ON v.lieferschein = l.id WHERE v.versendet_am=DATE_FORMAT(NOW(),'%Y-%m-%d') ".$this->app->erp->ProjektRechte('l.projekt')."")
);
$projectIds = implode(',', $subwherea);
$pkgsubwhere .= " AND l.projekt in ($projectIds)";
}
$this->app->Tpl->Set('PAKETE', $this->getPackages($pkgsubwhere));
if($daten['regs'])
{
if($subwherea)
@ -226,29 +211,14 @@ class Verkaufszahlen {
$this->app->Tpl->Parse('STATISTIKHEUTE','verkaufszahlen_statistik.tpl');
//gestern
$pkgsubwhere = "DATE(v.datum)=CURDATE()-1";
if($subwherea)
{
$pakete = $this->getPackages(
" v.versendet_am=DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 day),'%Y-%m-%d')
AND l.projekt in (".implode(', ', $subwherea).") ".$this->app->erp->ProjektRechte('l.projekt')
);
$this->app->Tpl->Set(
'PAKETE',
$pakete
//$this->app->DB->Select("SELECT COUNT(v.id) FROM versand v INNER JOIN lieferschein l ON v.lieferschein = l.id WHERE v.versendet_am=DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 day),'%Y-%m-%d') AND l.projekt in (".implode(', ', $subwherea).") ".$this->app->erp->ProjektRechte('l.projekt')."")
);
}else{
$pakete = $this->getPackages(
" v.versendet_am=DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 day),'%Y-%m-%d')
AND l.projekt in (".implode(', ', $subwherea).") ".$this->app->erp->ProjektRechte('l.projekt')
);
$this->app->Tpl->Set(
'PAKETE',
$pakete
//$this->app->DB->Select("SELECT COUNT(v.id) FROM versand v INNER JOIN lieferschein l ON v.lieferschein = l.id WHERE v.versendet_am=DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 day),'%Y-%m-%d') ".$this->app->erp->ProjektRechte('l.projekt')."")
);
$projectIds = implode(',', $subwherea);
$pkgsubwhere .= " AND l.projekt in ($projectIds)";
}
$this->app->Tpl->Set('PAKETE', $this->getPackages($pkgsubwhere));
if($daten['regs'])
{
if($subwherea)
@ -343,21 +313,19 @@ class Verkaufszahlen {
return [(float)$einnahmen_auftrag, (float)$deckungsbeitrag, (float)$deckungsbeitragprozent];
}
/**
* @param string $subwhere
* @param string $join
*
* @return int
*/
public function getPackages($subwhere, $join = '')
public function getPackages(string $subwhere, string $join = '', bool $applyProjectRights = true) : int
{
return (int)$this->app->DB->Select(
"SELECT COUNT(v.id)
FROM versand v
INNER JOIN lieferschein l ON v.lieferschein = l.id
$join
WHERE ".$subwhere
);
$sqlpackages = "
SELECT count(distinct v.id)
FROM versandpakete v
LEFT JOIN versandpaket_lieferschein_position vlp ON vlp.versandpaket = v.id
LEFT JOIN lieferschein_position lp ON vlp.lieferschein_position = lp.id
INNER JOIN lieferschein l ON l.id IN (lp.lieferschein, v.lieferschein_ohne_pos)
$join
WHERE $subwhere ";
if ($applyProjectRights)
$sqlpackages .= $this->app->erp->ProjektRechte('l.projekt');
return $this->app->DB->Select($sqlpackages);
}
/**
@ -689,8 +657,7 @@ class Verkaufszahlen {
}
//heute
$this->app->Tpl->Set('PAKETE',$this->app->DB->Select("SELECT COUNT(v.id) FROM versand v INNER JOIN lieferschein l ON v.lieferschein = l.id WHERE v.versendet_am=DATE_FORMAT(NOW(),'%Y-%m-%d') ".$this->app->erp->ProjektRechte('l.projekt').""));
$this->app->Tpl->Set('PAKETE',$this->getPackages("DATE(v.datum)=CURDATE()"));
$data = $this->app->DB->SelectArr("SELECT ifnull(SUM(umsatz_netto),0) as umsatz_netto2,ifnull(SUM(erloes_netto),0) as erloes_netto2 FROM `auftrag`
WHERE datum=DATE_FORMAT(NOW(),'%Y-%m-%d') AND ( status='abgeschlossen' OR status='freigegeben') ".
$this->app->erp->ProjektRechte('projekt').
@ -716,7 +683,7 @@ class Verkaufszahlen {
//gestern
$this->app->Tpl->Set('PAKETE',$this->app->DB->Select("SELECT COUNT(v.id) FROM versand v INNER JOIN lieferschein l ON v.lieferschein = l.id WHERE v.versendet_am=DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 day),'%Y-%m-%d') ".$this->app->erp->ProjektRechte('l.projekt').""));
$this->app->Tpl->Set('PAKETE',$this->getPackages("DATE(v.datum)=CURDATE()-1"));
$data = $this->app->DB->SelectArr("SELECT
ifnull(SUM(umsatz_netto),0) as umsatz_netto2,ifnull(SUM(erloes_netto),0) as erloes_netto2 FROM `auftrag`
@ -772,11 +739,13 @@ class Verkaufszahlen {
{
switch(strtoupper($type)) {
case 'TAGESUEBERSICHTPAKETE':
return $this->app->DB->SelectArrCache("SELECT DATE_FORMAT(v.versendet_am,'%d.%m.%Y') as datum,
count(v.id) as pakete
from versand v
INNER JOIN lieferschein l ON v.lieferschein = l.id
WHERE 1 ".$this->app->erp->ProjektRechte('l.projekt').' group by v.versendet_am',
return $this->app->DB->SelectArrCache("SELECT DATE_FORMAT(v.datum,'%d.%m.%Y') as datum,
count(distinct v.id) as pakete
FROM versandpakete v
LEFT JOIN versandpaket_lieferschein_position vlp ON vlp.versandpaket = v.id
LEFT JOIN lieferschein_position lp ON vlp.lieferschein_position = lp.id
INNER JOIN lieferschein l ON l.id IN (lp.lieferschein, v.lieferschein_ohne_pos)
WHERE 1 ".$this->app->erp->ProjektRechte('l.projekt').' group by DATE(v.datum)',
$seconds, 'verkaufszahlen'
);
break;

View File

@ -1,2 +1,4 @@
@layer reset {
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
/*# sourceMappingURL=normalize.min.css.map */
}

View File

@ -1,3 +1,10 @@
<!--
SPDX-FileCopyrightText: 2023 Andreas Palm
SPDX-FileCopyrightText: 2019 Xentral ERP Software GmbH, Fuggerstrasse 11, D-86150 Augsburg
SPDX-License-Identifier: LicenseRef-EGPL-3.1
-->
<!DOCTYPE html>
<html lang="de">
<head>
@ -97,6 +104,7 @@ $(document).ready(function() {
</style>
[FINALCSSLINKS]
[MODULEJAVASCRIPTHEAD]
[JAVASCRIPTMODULES]
[MODULESTYLESHEET]
</head>
<body class="[LAYOUTFIXMARKERCLASS]" data-module="[MODULE]" data-action="[ACTION]" data-version="[REVISION]">