OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

item_form.php

17 KB
<?php
$this->layout('layouts/main', [
    'page_title'    => $page_title,
    'current_route' => $current_route,
    'full_width'    => true,
    'extra_css'     => [
        '/assets/css/catalog.css?v=' . filemtime(dirname(__DIR__, 2) . '/public/assets/css/catalog.css'),
        '/assets/css/company-search.css',
    ],
    'extra_js'      => ['/assets/js/company-search.js'],
]);
// Der Vorschlag, eine unbekannte Firma anzulegen, setzt das Adressbuch und
// das Recht voraus, es zu pflegen.
$kannFirmaAnlegen = !empty($supplier_options) && ($modules['contacts'] ?? false)
    && $this->hasCapability('manage_contacts');
$item    = $item ?? null;
$id      = $item ? (int)$item['id'] : 0;
$action  = $id > 0 ? '/catalog/items/' . $id : '/catalog/items';
$val     = fn($k, $d = '') => $this->e($item[$k] ?? $d);
$accIds  = $accessory_ids ?? [];
$reqs    = $requirements ?? [];
$billing = $billing ?? ['once' => 'einmalig', 'monthly' => 'pro Monat', 'yearly' => 'pro Jahr'];
$reqOptions = '<option value="">– Artikel wählen –</option>';
foreach (($all_items ?? []) as $ai) {
    if ((int)$ai['id'] === $id) { continue; }
    $reqOptions .= '<option value="' . (int)$ai['id'] . '">' . htmlspecialchars((string)$ai['name'], ENT_QUOTES) . '</option>';
}
?>
<div class="cat-wrap">
    <div class="content-header">
        <div>
            <h1><?= $id > 0 ? 'Artikel bearbeiten' : 'Neuer Artikel' ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/catalog"><?= $this->te('Ausstattung &amp; Preise') ?></a></li>
                <li class="breadcrumb-item active"><?= $id > 0 ? $this->e($item['name']) : 'Neu' ?></li>
            </ol>
        </div>
        <div class="content-header-actions">
            <a href="<?= $id > 0 ? '/catalog/items/' . $id : '/catalog' ?>" class="btn btn-default"><i class="fas fa-arrow-left"></i> <?= $this->te('Abbrechen') ?></a>
        </div>
    </div>

    <div class="page-body">
        <form method="post" action="<?= $action ?>" enctype="multipart/form-data">
            <input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
            <div class="detail-card">
                <div class="detail-card-body">
                    <div class="form-row-2">
                        <div class="form-group">
                            <label for="f-name"><?= $this->te('Name') ?> <span class="text-danger">*</span></label>
                            <input type="text" id="f-name" name="name" class="form-control" maxlength="255" required value="<?= $val('name') ?>" placeholder="<?= $this->te('z. B. Standard-Laptop 14&quot; Business') ?>">
                        </div>
                        <div class="form-group">
                            <label for="f-cat"><?= $this->te('Kategorie') ?></label>
                            <select id="f-cat" name="category_id" class="form-control" data-searchable>
                                <option value=""><?= $this->te('– keine –') ?></option>
                                <?php foreach (($categories ?? []) as $c): ?>
                                <option value="<?= (int)$c['id'] ?>" <?= (int)($item['category_id'] ?? 0) === (int)$c['id'] ? 'selected' : '' ?>><?= $this->e($c['name']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                    </div>
                    <div class="form-row-2">
                        <div class="form-group">
                            <label for="f-man"><?= $this->te('Hersteller') ?></label>
                            <input type="text" id="f-man" name="manufacturer" class="form-control" maxlength="150" value="<?= $val('manufacturer') ?>">
                        </div>
                        <div class="form-group">
                            <label for="f-model"><?= $this->te('Modell') ?></label>
                            <input type="text" id="f-model" name="model" class="form-control" maxlength="150" value="<?= $val('model') ?>">
                        </div>
                    </div>
                    <div class="form-row-2">
                        <div class="form-group">
                            <label for="f-assettype"><?= $this->te('Inventar-Modell verknüpfen') ?></label>
                            <select id="f-assettype" name="asset_type_id" class="form-control" data-searchable>
                                <option value=""><?= $this->te('– kein Modell –') ?></option>
                                <?php foreach (($asset_types ?? []) as $t): ?>
                                <option value="<?= (int)$t['id'] ?>"
                                        data-name="<?= $this->e($t['name']) ?>"
                                        data-man="<?= $this->e($t['manufacturer'] ?? '') ?>"
                                        data-spec="<?= $this->e($t['specs'] ?? '') ?>"
                                        data-image="<?= $this->e($t['image'] ?? '') ?>"
                                        <?= (int)($item['asset_type_id'] ?? 0) === (int)$t['id'] ? 'selected' : '' ?>>
                                    <?= $this->e(trim(($t['manufacturer'] ?? '') . ' ' . $t['name'])) ?>
                                </option>
                                <?php endforeach; ?>
                            </select>
                            <small class="form-hint"><?= $this->te('Übernimmt beim Auswählen automatisch Bild sowie Name, Hersteller, Modell und Spezifikation als Vorbelegung (überschreibbar).') ?>
                                <?php if (empty($asset_types)): ?><br><?= $this->te('Noch keine Inventar-Modelle vorhanden.') ?><?php endif; ?>
                            </small>
                        </div>
                        <div class="form-group">
                            <label><?= $this->te('Bild') ?></label>
                            <div class="cat-img-row">
                                <div class="cat-img-preview" id="f-img-preview">
                                    <?php if (!empty($item['image_effective'])): ?><img src="<?= $this->e($item['image_effective']) ?>" alt=""><?php else: ?><span class="text-muted"><?= $this->te('Kein Bild') ?></span><?php endif; ?>
                                </div>
                                <div class="cat-img-fields">
                                    <input type="file" name="item_image" id="f-img" class="form-control form-control-sm" accept="image/jpeg,image/png,image/gif,image/webp">
                                    <small class="form-hint"><?= $this->te('Eigenes Bild (JPG/PNG/GIF/WebP) – hat Vorrang vor dem Modellbild.') ?></small>
                                    <?php if (!empty($item['image_path'])): ?>
                                    <label class="cat-img-remove"><input type="checkbox" name="remove_image" value="1"> <?= $this->te('Eigenes Bild entfernen') ?></label>
                                    <?php endif; ?>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="form-row-2">
                        <div class="form-group">
                            <label for="f-sku"><?= $this->te('Artikel-Nr. (SKU)') ?></label>
                            <input type="text" id="f-sku" name="sku" class="form-control" maxlength="100" value="<?= $val('sku') ?>">
                        </div>
                        <div class="form-group">
                            <label for="f-unit"><?= $this->te('Einheit') ?></label>
                            <input type="text" id="f-unit" name="unit" class="form-control" maxlength="30" value="<?= $val('unit', 'Stück') ?>">
                        </div>
                    </div>
                    <div class="form-row-2">
                        <div class="form-group">
                            <label for="f-cycle"><?= $this->te('Abrechnung') ?></label>
                            <select id="f-cycle" name="billing_cycle" class="form-control" data-searchable>
                                <?php foreach ($billing as $bk => $bl): ?>
                                <option value="<?= $this->e($bk) ?>" <?= ($item['billing_cycle'] ?? 'once') === $bk ? 'selected' : '' ?>><?= $this->e($bl) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <small class="form-hint"><?= $this->te('„pro Monat/Jahr" z. B. für Abo-Lizenzen (M365, Adobe …).') ?></small>
                        </div>
                        <div class="form-group">
                            <label for="f-sup"><?= $this->te('Standard-Lieferant') ?></label>
                            <?php if (!empty($supplier_options)): ?>
                            <select id="f-sup" name="default_supplier_company_id" class="form-control" data-searchable <?= $kannFirmaAnlegen ? 'data-create-company' : '' ?>>
                                <option value=""><?= $this->te('– keiner –') ?></option>
                                <?php foreach ($supplier_options as $so): ?>
                                <option value="<?= (int)$so['id'] ?>" <?= (int)($item['default_supplier_company_id'] ?? 0) === (int)$so['id'] ? 'selected' : '' ?>><?= $this->e($so['name']) ?></option>
                                <?php endforeach; ?>
                            </select>
                            <small class="form-hint"><?= $this->te('Bestimmt den in der Übersicht gezeigten Preis.') ?></small>
                            <?php else: ?>
                            <input type="text" class="form-control" value="" placeholder="<?= $this->te('Kontakte-Modul für Firmenauswahl aktivieren') ?>" disabled>
                            <?php endif; ?>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="f-syn"><?= $this->te('Synonyme / Suchbegriffe') ?></label>
                        <input type="text" id="f-syn" name="synonyms" class="form-control" maxlength="255" value="<?= $val('synonyms') ?>" placeholder="<?= $this->te('z. B. Notebook, Ultrabook') ?>">
                        <small class="form-hint"><?= $this->te('Kommagetrennt – damit die Suche den Artikel auch unter anderen Begriffen findet.') ?></small>
                    </div>
                    <div class="form-group">
                        <label for="f-spec"><?= $this->te('Spezifikation') ?></label>
                        <textarea id="f-spec" name="spec" class="form-control" rows="3" placeholder="<?= $this->te('CPU, RAM, Displaygröße …') ?>"><?= $val('spec') ?></textarea>
                    </div>
                    <div class="form-group">
                        <label for="f-notes"><?= $this->te('Interne Notiz') ?></label>
                        <textarea id="f-notes" name="notes" class="form-control" rows="2"><?= $val('notes') ?></textarea>
                    </div>
                    <div class="form-group">
                        <label for="f-acc"><?= $this->te('Typisches Zubehör') ?></label>
                        <select id="f-acc" name="accessories[]" class="form-control" multiple size="6">
                            <?php foreach (($all_items ?? []) as $ai): if ((int)$ai['id'] === $id) { continue; } ?>
                            <option value="<?= (int)$ai['id'] ?>" <?= in_array((int)$ai['id'], $accIds, true) ? 'selected' : '' ?>><?= $this->e($ai['name']) ?></option>
                            <?php endforeach; ?>
                        </select>
                        <small class="form-hint"><?= $this->te('Mehrfachauswahl (Strg/Cmd) – erscheint auf der Detailseite als „Typisches Zubehör".') ?></small>
                    </div>
                    <div class="form-group">
                        <label><?= $this->te('Pflicht-Abhängigkeiten') ?></label>
                        <div id="req-rows"></div>
                        <button type="button" class="btn btn-default btn-sm" id="req-add"><i class="fas fa-plus"></i> <?= $this->te('Pflicht-Position hinzufügen') ?></button>
                        <small class="form-hint"><?= $this->te('Zwingend benötigte Artikel/Lizenzen (z. B. Smartphone → 1× MDM-Lizenz). Werden auf der Detailseite, in der Preisliste, Kalkulation und in Profilen automatisch mitgeführt und mitgerechnet.') ?></small>
                    </div>
                    <div class="settings-toggles">
                        <label class="settings-toggle">
                            <input type="checkbox" name="is_standard" value="1" <?= (int)($item['is_standard'] ?? 0) === 1 ? 'checked' : '' ?>>
                            <span class="toggle-text"><strong><?= $this->te('Standard-Artikel der Kategorie') ?></strong> <small><?= $this->te('Wird hervorgehoben; je Kategorie nur einer.') ?></small></span>
                        </label>
                        <label class="settings-toggle">
                            <input type="checkbox" name="is_active" value="1" <?= (int)($item['is_active'] ?? 1) === 1 ? 'checked' : '' ?>>
                            <span class="toggle-text"><strong><?= $this->te('Aktiv') ?></strong> <small><?= $this->te('Nur aktive Artikel erscheinen in Katalog und Kalkulation.') ?></small></span>
                        </label>
                    </div>
                </div>
            </div>
            <div class="cat-form-actions">
                <button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> <?= $this->te('Speichern') ?></button>
                <a href="<?= $id > 0 ? '/catalog/items/' . $id : '/catalog' ?>" class="btn btn-default"><?= $this->te('Abbrechen') ?></a>
            </div>
        </form>
    </div>
</div>

<template id="req-row-tpl">
    <div class="cat-brow">
        <select name="req_item_id[]" class="form-control" data-searchable><?= $reqOptions ?></select>
        <input type="number" name="req_qty[]" class="form-control cat-brow-qty" value="1" min="1" title="<?= $this->te('Menge') ?>">
        <input type="text" name="req_note[]" class="form-control cat-brow-note" maxlength="255" placeholder="<?= $this->te('Notiz (optional)') ?>">
        <button type="button" class="btn btn-outline-danger btn-sm cat-brow-rm" title="<?= $this->te('Entfernen') ?>">&times;</button>
    </div>
</template>

<script>
(function () {
    var host = document.getElementById('req-rows');
    var tpl  = document.getElementById('req-row-tpl');
    if (!host || !tpl) { return; }
    function addRow(itemId, qty, note) {
        var node = tpl.content.cloneNode(true);
        var sel  = node.querySelector('select');
        var q    = node.querySelector('input[name="req_qty[]"]');
        var n    = node.querySelector('input[name="req_note[]"]');
        if (itemId) { sel.value = String(itemId); }
        if (qty)    { q.value = qty; }
        if (note)   { n.value = note; }
        node.querySelector('.cat-brow-rm').addEventListener('click', function () { this.closest('.cat-brow').remove(); });
        host.appendChild(node);
        // Nachträglich eingefügte Zeile: durchsuchbares Dropdown aufwerten
        // (beim Erst-Rendern übernimmt das die globale Initialisierung).
        if (window.ssInit) { window.ssInit(host); }
    }
    document.getElementById('req-add').addEventListener('click', function () { addRow(); });
    var existing = <?= json_encode(array_map(fn($r) => ['id' => (int)$r['id'], 'qty' => (int)$r['quantity'], 'note' => (string)($r['note'] ?? '')], $reqs), JSON_UNESCAPED_UNICODE) ?>;
    existing.forEach(function (r) { addRow(r.id, r.qty, r.note); });
})();

// Inventar-Modell verknüpfen: automatische Vorbelegung (Bild + Stammdaten).
(function () {
    var sel = document.getElementById('f-assettype');
    var preview = document.getElementById('f-img-preview');
    var fileInput = document.getElementById('f-img');
    var hasOwn = <?= !empty($item['image_path']) ? 'true' : 'false' ?>;
    function setPreviewImg(src) {
        preview.innerHTML = src ? '<img src="' + src + '" alt="">' : '<span class="text-muted">Kein Bild</span>';
    }
    if (sel) {
        sel.addEventListener('change', function () {
            var opt = this.options[this.selectedIndex];
            if (!opt || !opt.value) {
                if (!hasOwn) { setPreviewImg(''); }
                return;
            }
            // Stammdaten als Vorbelegung übernehmen (überschreibbar).
            var man   = opt.getAttribute('data-man')  || '';
            var model = opt.getAttribute('data-name') || ''; // asset_type.name = Modellbezeichnung
            var spec  = opt.getAttribute('data-spec') || '';
            var nameEl  = document.getElementById('f-name');
            var manEl   = document.getElementById('f-man');
            var modelEl = document.getElementById('f-model');
            var specEl  = document.getElementById('f-spec');
            if (manEl)   { manEl.value   = man; }
            if (modelEl) { modelEl.value = model; }
            if (specEl)  { specEl.value  = spec; }
            if (nameEl)  { nameEl.value  = (man + ' ' + model).trim(); }
            // Bildvorschau nur, wenn kein eigenes Bild Vorrang hat.
            if (!hasOwn) { setPreviewImg(opt.getAttribute('data-image') || ''); }
        });
    }
    if (fileInput) {
        fileInput.addEventListener('change', function () {
            var f = this.files && this.files[0];
            if (f) { setPreviewImg(URL.createObjectURL(f)); hasOwn = true; }
        });
    }
})();
</script>

<?php if ($kannFirmaAnlegen): ?>
<?= $this->render('partials/_company_quick_create') ?>
<?php endif; ?>