-
+
diff --git a/www/pages/matrixprodukt.php b/www/pages/matrixprodukt.php
new file mode 100644
index 00000000..2cecd762
--- /dev/null
+++ b/www/pages/matrixprodukt.php
@@ -0,0 +1,346 @@
+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->app->ActionHandlerInit($this);
+ $this->app->ActionHandler("list", "ActionList");
+ $this->app->ActionHandler("optionenlist", "ActionOptionList");
+ $this->app->ActionHandler("artikel", "ActionArticle");
+ $this->app->ActionHandlerListen($app);
+ }
+
+ private function createMenu(): void
+ {
+ $this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=list", "Übersicht");
+ }
+
+ 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ü');
+ $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 = "
";
+ $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ü');
+ $width = array('1%', '30%', '30%', '1%');
+ $findcols = array('mo.id', 'mo.name', 'mo.name_ext');
+ $searchsql = array('mo.name', 'mo.name_ext');
+ $menu = "
"
+ . "Conf->WFconf['defaulttheme']}/images/edit.svg\"> "
+ . "Conf->WFconf['defaulttheme']}/images/delete.svg\">"
+ . " |
";
+ $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ü');
+ $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 = "
";
+ $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ü');
+ $width = array('1%', '30%', '30%', '1%');
+ $findcols = array('moa.id', 'moa.name', 'moa.name_ext');
+ $searchsql = array('moa.name', 'moa.name_ext');
+ $menu = "
"
+ . "Conf->WFconf['defaulttheme']}/images/edit.svg\"> "
+ . "Conf->WFconf['defaulttheme']}/images/delete.svg\">"
+ . " |
";
+ $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 = [];
+ $optIdColumns = [];
+ $optNameColumns = [];
+ $optFrom = [];
+ $optWhere = [];
+ foreach ($groups as $groupId => $groupName) {
+ $heading[] = $groupName;
+ $width[] = '5%';
+ $nameColumns[] = "name_$groupId";
+ $optIdColumns[] = "moa_$groupId.id";
+ $optNameColumns[] = "moa_$groupId.name as name_$groupId";
+ $optFrom[] = "matrixprodukt_eigenschaftenoptionen_artikel moa_$groupId";
+ $optWhere[] = "moa_$groupId.artikel = $id AND moa_$groupId.gruppe = $groupId";
+ }
+ $heading[] = 'Menü';
+ $width[] = '1%';
+ $findcols = array_merge($nameColumns, ['nummer']);
+ $searchsql = $nameColumns;
+ $menu = "
"
+ . "Conf->WFconf['defaulttheme']}/images/edit.svg\"> "
+ . "Conf->WFconf['defaulttheme']}/images/delete.svg\">"
+ . " |
";
+ $optsqlIdCols = join(',', $optIdColumns);
+ $optsqlNameCols = join(',', $optNameColumns);
+ $optsqlFrom = join(' JOIN ', $optFrom);
+ $optsqlWhere = join(' AND ', $optWhere);
+ $optsql = "SELECT CONCAT_WS(',', $optsqlIdCols) idlist, $optsqlNameCols
+ FROM $optsqlFrom WHERE $optsqlWhere";
+ $artsql = "SELECT a.id, a.nummer, group_concat(mota.option_id order by mota.option_id separator ',') idlist
+ FROM matrixprodukt_optionen_zu_artikel mota
+ JOIN artikel a ON mota.artikel = a.id WHERE a.variante_von = $id group by mota.artikel";
+ $sqlNameCols = join(',', $nameColumns);
+ $sql = "SELECT SQL_CALC_FOUND_ROWS art.id, art.nummer, $sqlNameCols, art.id
+ FROM ($artsql) art
+ LEFT OUTER JOIN ($optsql) opts ON opts.idlist = art.idlist";
+ $where = "1";
+ //$count = "SELECT count(DISTINCT moa.id)
+// FROM matrixprodukt_eigenschaftengruppen_artikel mga
+ // LEFT OUTER JOIN matrixprodukt_eigenschaftenoptionen_artikel moa on moa.gruppe = mga.id 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', '
');
+ $this->app->YUI->TableSearch('TAB1', 'matrixprodukt_groups', "show", "", "", basename(__FILE__), __CLASS__);
+ $this->app->Tpl->Parse('PAGE', "matrixprodukt_list.tpl");
+ }
+
+ public function ActionOptionList()
+ {
+ $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();
+ }
+
+ $this->app->Tpl->Set('TABSADD', '
');
+ $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 "acarticles":
+ $query = $this->app->Secure->GetGET('query');
+ $result = $this->app->DB->SelectArr("SELECT id, nummer, name_de FROM artikel WHERE (nummer LIKE '%$query%' OR name_de LIKE '%$query%') AND geloescht = 0");
+ return new JsonResponse($result);
+ }
+
+ $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 {
+ $articleId = $this->app->Secure->GetGET('id');
+ $this->app->erp->MenuEintrag("index.php?module=matrixprodukt&action=artikel&id=$articleId", 'Zurück zur Gruppenü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');
+ }
+ $this->app->YUI->TableSearch('TAB2', 'matrixprodukt_variants', "show", "", "", basename(__FILE__), __CLASS__);
+ $this->app->Tpl->Parse('PAGE', "matrixprodukt_article.tpl");
+ }
+}
\ No newline at end of file
diff --git a/www/themes/new/css/normalize.min.css b/www/themes/new/css/normalize.min.css
index 8a52fe3c..03001247 100644
--- a/www/themes/new/css/normalize.min.css
+++ b/www/themes/new/css/normalize.min.css
@@ -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 */
+}
\ No newline at end of file
diff --git a/www/themes/new/css/styles.css b/www/themes/new/css/styles.css
index f4d39782..a498d0b3 100644
--- a/www/themes/new/css/styles.css
+++ b/www/themes/new/css/styles.css
@@ -1418,7 +1418,7 @@ ul.menu.merged[data-items='25'] > li { width: 4%; }
}
-/* Clear Floated Elements
+/* Clear Floated Elements
- - - - - - - - - - - - - - - - - - - - - - */
.clear {
@@ -1725,7 +1725,7 @@ fieldset.usersave div.filter-item > label {
}
-/* Calendar
+/* Calendar
- - - - - - - - - - - - - - - - - - - - - - */
diff --git a/www/themes/new/templates/page.tpl b/www/themes/new/templates/page.tpl
index 84673c22..ecbefd81 100644
--- a/www/themes/new/templates/page.tpl
+++ b/www/themes/new/templates/page.tpl
@@ -1,3 +1,10 @@
+
+
@@ -97,6 +104,7 @@ $(document).ready(function() {
[FINALCSSLINKS]
[MODULEJAVASCRIPTHEAD]
+[JAVASCRIPTMODULES]
[MODULESTYLESHEET]