added tenant configuration
This commit is contained in:
@@ -8,4 +8,23 @@ class WorkorderCompanyModel extends TTCrudBaseModel {
|
|||||||
public ?string $visibleForAddressId;
|
public ?string $visibleForAddressId;
|
||||||
public int $create;
|
public int $create;
|
||||||
public int $createBy;
|
public int $createBy;
|
||||||
|
|
||||||
|
public static function getCompanyWorkers(int $companyId): array {
|
||||||
|
if (!$company = self::get($companyId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = self::getDB();
|
||||||
|
$addressId = $db->real_escape_string($company->addressId);
|
||||||
|
|
||||||
|
$sql = "SELECT w.id, w.name, w.email
|
||||||
|
FROM `" . FRONKDB_DBNAME . "`.`Worker` w
|
||||||
|
JOIN `" . FRONKDB_DBNAME . "`.`WorkerPermission` wp ON w.id = wp.worker_id
|
||||||
|
WHERE w.address_id = '$addressId' AND wp.canRMLCompany = 'true' AND w.active = 1
|
||||||
|
ORDER BY w.name ASC";
|
||||||
|
|
||||||
|
$result = $db->query($sql);
|
||||||
|
|
||||||
|
return $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class WorkorderTenantConfigController extends TTCrud {
|
||||||
|
protected string $headerTitle = 'Mandanten & Firmen Konfiguration';
|
||||||
|
protected bool $createText = false;
|
||||||
|
protected array $columns = [];
|
||||||
|
|
||||||
|
protected function indexAction() {
|
||||||
|
Helper::renderVue($this, 'WorkorderTenantConfig', $this->headerTitle, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTenantConfigsAction() {
|
||||||
|
$configs = WorkorderTenantConfigModel::getAll([], null, 0, ['key' => 'name', 'order' => 'ASC']);
|
||||||
|
self::returnJson($configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function saveTenantConfigAction() {
|
||||||
|
$data = $this->postData;
|
||||||
|
$data['documentationTypes'] = json_encode($data['documentationTypes'] ?? []);
|
||||||
|
$data['interventionTypes'] = json_encode($data['interventionTypes'] ?? []);
|
||||||
|
$data['workorderCreationFilters'] ??= '{}';
|
||||||
|
|
||||||
|
if (empty($data['id'])) {
|
||||||
|
$data['create'] = time();
|
||||||
|
$data['createBy'] = $this->user->id;
|
||||||
|
WorkorderTenantConfigModel::create($data);
|
||||||
|
} else {
|
||||||
|
WorkorderTenantConfigModel::update($data);
|
||||||
|
}
|
||||||
|
self::returnJson(['success' => true, 'message' => 'Mandanten-Konfiguration gespeichert.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function deleteTenantConfigAction() {
|
||||||
|
if (empty($this->postData['id'])) self::sendError("ID fehlt.");
|
||||||
|
|
||||||
|
WorkorderTenantConfigModel::delete($this->postData['id']);
|
||||||
|
self::returnJson(['success' => true, 'message' => 'Mandanten-Konfiguration gelöscht.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCompaniesAction() {
|
||||||
|
$companies = WorkorderCompanyModel::getAll([], null, 0, ['key' => 'name', 'order' => 'ASC']);
|
||||||
|
foreach ($companies as $company) {
|
||||||
|
$company->workers = WorkorderCompanyModel::getCompanyWorkers($company->id);
|
||||||
|
}
|
||||||
|
self::returnJson($companies);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function saveCompanyAction() {
|
||||||
|
$data = $this->postData;
|
||||||
|
if (empty($data['name']) || empty($data['addressId'])) self::sendError("Name und Adresse sind erforderlich.");
|
||||||
|
|
||||||
|
unset($data['workers']);
|
||||||
|
$data['visibleForAddressId'] = json_encode($data['visibleForAddressId'] ?? []);
|
||||||
|
|
||||||
|
if (empty($data['id'])) {
|
||||||
|
$data['create'] = time();
|
||||||
|
$data['createBy'] = $this->user->id;
|
||||||
|
WorkorderCompanyModel::create($data);
|
||||||
|
} else {
|
||||||
|
WorkorderCompanyModel::update($data);
|
||||||
|
}
|
||||||
|
self::returnJson(['success' => true, 'message' => 'Firma gespeichert.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function deleteCompanyAction() {
|
||||||
|
if (empty($this->postData['id'])) self::sendError("ID fehlt.");
|
||||||
|
|
||||||
|
WorkorderCompanyModel::delete($this->postData['id']);
|
||||||
|
self::returnJson(['success' => true, 'message' => 'Firma gelöscht.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function addressAutocompleteAction() {
|
||||||
|
$search = trim($this->request->q ?? '');
|
||||||
|
$searchedID = $this->request->searchedID ?? null;
|
||||||
|
$addresses = [];
|
||||||
|
|
||||||
|
if ($searchedID) {
|
||||||
|
$ids = array_filter(explode(',', $searchedID));
|
||||||
|
if ($ids) $addresses = AddressModel::search(['id' => $ids]);
|
||||||
|
} elseif (strlen($search) >= 2) {
|
||||||
|
$addresses = array_slice(AddressModel::search(["company" => $search]), 0, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = array_map(function($address) {
|
||||||
|
return [
|
||||||
|
'value' => $address->id,
|
||||||
|
'text' => "{$address->getCompanyOrName()} ({$address->zip} {$address->city})"
|
||||||
|
];
|
||||||
|
}, $addresses);
|
||||||
|
|
||||||
|
self::returnJson($results);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/* WorkorderTenantConfig.css */
|
||||||
|
|
||||||
|
.config-card .card-header {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-card .card-body h6 {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-card .list-group-item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
border-color: #f1f1f1;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-list .list-group-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding-top: .4rem;
|
||||||
|
padding-bottom: .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-list .list-group-item span {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-list .list-group-item small.code-font {
|
||||||
|
font-family: monospace;
|
||||||
|
color: #6c757d;
|
||||||
|
background-color: #e9ecef;
|
||||||
|
padding: 0.1rem 0.4rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tenant-tags-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
min-height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tenant-tag {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.3em 0.75em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tenant-tag i.fa-times-circle {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tenant-tag i.fa-times-circle:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tenant-edit-list .list-group-item {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tenant-edit-list .btn-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
// WorkorderTenantConfig.js
|
||||||
|
Vue.component('workorder-tenant-config', {
|
||||||
|
template: `
|
||||||
|
<tt-card>
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
<li class="nav-item"><a class="nav-link" :class="{active: activeTab === 'configs'}" href="#"
|
||||||
|
@click.prevent="activeTab = 'configs'"><i class="fas fa-cogs mr-1"></i>
|
||||||
|
Mandanten-Konfigurationen</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" :class="{active: activeTab === 'companies'}" href="#"
|
||||||
|
@click.prevent="activeTab = 'companies'"><i class="fas fa-building mr-1"></i>
|
||||||
|
Firmenverwaltung</a></li>
|
||||||
|
</ul>
|
||||||
|
<tt-button :text="activeTab === 'configs' ? 'Neue Konfiguration' : 'Neue Firma'" @click="openCreateModal"
|
||||||
|
icon="fas fa-plus" additional-class="btn-primary"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="loading" class="text-center p-5"><i class="fas fa-spinner fa-spin fa-2x"></i></div>
|
||||||
|
|
||||||
|
<div v-show="!loading && activeTab === 'configs'">
|
||||||
|
<div v-for="config in configs" :key="config.id" class="card mb-3 config-card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="fas fa-file-alt mr-2"></i>
|
||||||
|
<tt-autocomplete v-if="editingId === config.id" :api-url="addressApiUrl"
|
||||||
|
v-model="editableItem.addressId" @input="editableItem.name = $event.text" no-form-group
|
||||||
|
sm/>
|
||||||
|
<strong v-else>{{ config.name }}</strong>
|
||||||
|
</h5>
|
||||||
|
<div>
|
||||||
|
<tt-button v-if="editingId !== config.id" @click="startEdit(config)" icon="fas fa-edit"
|
||||||
|
additional-class="btn-sm btn-outline-primary mr-2"/>
|
||||||
|
<tt-button v-if="editingId === config.id" @click="saveItem" icon="fas fa-save"
|
||||||
|
additional-class="btn-sm btn-success mr-2"/>
|
||||||
|
<tt-button v-if="editingId === config.id" @click="cancelEdit" icon="fas fa-times"
|
||||||
|
additional-class="btn-sm btn-secondary mr-2"/>
|
||||||
|
<tt-button @click="deleteItem(config, 'configs')" icon="fas fa-trash"
|
||||||
|
additional-class="btn-sm btn-outline-danger"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h6>Dokumentationstypen</h6>
|
||||||
|
<tt-positions-manager :config="docTypesConfig" v-model="editableItem.documentationTypes"
|
||||||
|
v-if="editingId === config.id"/>
|
||||||
|
<ul v-else class="list-group list-group-flush type-list">
|
||||||
|
<li v-for="doc in JSON.parse(config.documentationTypes || '[]')" class="list-group-item">
|
||||||
|
<span>{{ doc.text }}</span><small class="code-font">{{ doc.value }}</small></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h6>Interventionstypen</h6>
|
||||||
|
<tt-positions-manager :config="interventionTypesConfig" v-model="editableItem.interventionTypes"
|
||||||
|
v-if="editingId === config.id"/>
|
||||||
|
<ul v-else class="list-group list-group-flush type-list">
|
||||||
|
<li v-for="intervention in JSON.parse(config.interventionTypes || '[]')" class="list-group-item">
|
||||||
|
<span>{{ intervention.text }}</span><small class="code-font">{{ intervention.value }}</small></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6>Filter für Auftragserstellung</h6>
|
||||||
|
<pre v-if="editingId !== config.id"
|
||||||
|
class="code-block">{{ JSON.stringify(JSON.parse(config.workorderCreationFilters || '{}'), null, 2) }}</pre>
|
||||||
|
<tt-textarea v-else v-model="editableJsonFilter" rows="6"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6 class="mb-3">Optionen</h6>
|
||||||
|
<tt-checkbox label="Dokumentation für Tiefbau erforderlich"
|
||||||
|
v-model="editableItem.civilEngineeringDocsRequired" sm v-if="editingId === config.id"/>
|
||||||
|
<p v-else>Tiefbau-Doku: {{ config.civilEngineeringDocsRequired ? 'Ja' : 'Nein' }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h6>Zugeordnete Firmen</h6>
|
||||||
|
<ul v-if="companiesByTenantMap[config.addressId] && companiesByTenantMap[config.addressId].length"
|
||||||
|
class="list-group">
|
||||||
|
<li v-for="company in companiesByTenantMap[config.addressId]" class="list-group-item py-1"><i
|
||||||
|
class="fas fa-building mr-2 text-muted"></i>{{ company.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-else class="text-muted p-3">Keine Firmen zugeordnet.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="!loading && activeTab === 'companies'">
|
||||||
|
<div v-for="company in companies" :key="company.id" class="card mb-3 config-card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-building mr-2"></i><strong>{{ company.name }}</strong></h5>
|
||||||
|
<div>
|
||||||
|
<tt-button v-if="editingId !== company.id" @click="startEdit(company)" icon="fas fa-edit"
|
||||||
|
additional-class="btn-sm btn-outline-primary mr-2"/>
|
||||||
|
<tt-button v-if="editingId === company.id" @click="saveItem" icon="fas fa-save"
|
||||||
|
additional-class="btn-sm btn-success mr-2"/>
|
||||||
|
<tt-button v-if="editingId === company.id" @click="cancelEdit" icon="fas fa-times"
|
||||||
|
additional-class="btn-sm btn-secondary mr-2"/>
|
||||||
|
<tt-button @click="deleteItem(company, 'companies')" icon="fas fa-trash"
|
||||||
|
additional-class="btn-sm btn-outline-danger"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h6>Sichtbar für Mandanten</h6>
|
||||||
|
<div v-if="editingId === company.id">
|
||||||
|
<ul class="list-group tenant-edit-list mb-2">
|
||||||
|
<li v-for="(tenantId, index) in editableItem.visibleForAddressId" :key="tenantId"
|
||||||
|
class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<tt-resolver :value="tenantId" reference="/WorkorderTenantConfig/addressAutocomplete"
|
||||||
|
:autocomplete="true"/>
|
||||||
|
<tt-button icon="fas fa-trash" @click="removeTenant(index)"
|
||||||
|
additional-class="btn-sm btn-link text-danger"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<tt-autocomplete :api-url="addressApiUrl" v-model="tenantToAdd" @input="addTenant"
|
||||||
|
placeholder="Mandant hinzufügen..." sm no-form-group/>
|
||||||
|
</div>
|
||||||
|
<ul v-else class="list-group list-group-flush">
|
||||||
|
<li v-for="tenantId in JSON.parse(company.visibleForAddressId || '[]')"
|
||||||
|
class="list-group-item py-1">
|
||||||
|
<tt-resolver :value="tenantId" reference="/WorkorderTenantConfig/addressAutocomplete"
|
||||||
|
:autocomplete="true"/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h6>Zugehörige Mitarbeiter <small>(mit RMLCompany-Recht)</small></h6>
|
||||||
|
<ul v-if="company.workers && company.workers.length" class="list-group list-group-flush">
|
||||||
|
<li v-for="worker in company.workers" class="list-group-item py-1">{{ worker.name }} (
|
||||||
|
{{ worker.email }})
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-else class="text-muted p-3">Keine Mitarbeiter für diese Firma gefunden.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<tt-modal v-if="showModal" :show.sync="showModal" :title="modalTitle" @submit="saveNewItem" :delete="false">
|
||||||
|
<div v-if="activeTab === 'configs'">
|
||||||
|
<tt-autocomplete label="Mandant" :api-url="addressApiUrl" v-model="newItem.addressId"
|
||||||
|
@input="newItem.name = $event.text" sm row required/>
|
||||||
|
</div>
|
||||||
|
<div v-if="activeTab === 'companies'">
|
||||||
|
<tt-autocomplete label="Firma" :api-url="addressApiUrl" v-model="newItem.addressId"
|
||||||
|
@input="newItem.name = $event.text" sm row required/>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-form-label col-sm-4 col-form-label-sm">Sichtbar für Mandanten</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="tenant-tags-container mb-2">
|
||||||
|
<span v-for="(tenantId, index) in newItem.visibleForAddressId" :key="tenantId"
|
||||||
|
class="badge badge-primary tenant-tag">
|
||||||
|
<tt-resolver :value="tenantId" reference="/WorkorderTenantConfig/addressAutocomplete"
|
||||||
|
:autocomplete="true"/>
|
||||||
|
<i class="fas fa-times-circle" @click="removeTenant(index, true)"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<tt-autocomplete :api-url="addressApiUrl" v-model="tenantToAdd" @input="addTenant($event, true)"
|
||||||
|
placeholder="Mandant hinzufügen..." sm no-form-group/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</tt-modal>
|
||||||
|
</tt-card>
|
||||||
|
`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true, activeTab: 'configs', configs: [], companies: [], editingId: null, editableItem: {},
|
||||||
|
editableJsonFilter: '', showModal: false, newItem: {}, tenantToAdd: null,
|
||||||
|
addressApiUrl: `${window.TT_CONFIG.BASE_PATH}/WorkorderTenantConfig/addressAutocomplete`,
|
||||||
|
docTypesConfig: {
|
||||||
|
fields: {
|
||||||
|
text: {type: 'input', label: 'Anzeigename'},
|
||||||
|
value: {type: 'input', label: 'Schlüssel'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interventionTypesConfig: {
|
||||||
|
fields: {
|
||||||
|
text: {type: 'input', label: 'Anzeigename'},
|
||||||
|
value: {type: 'input', label: 'Schlüssel'}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalTitle() {
|
||||||
|
return this.activeTab === 'configs' ? 'Neue Konfiguration erstellen' : 'Neue Firma anlegen';
|
||||||
|
},
|
||||||
|
companiesByTenantMap() {
|
||||||
|
const map = {};
|
||||||
|
this.configs.forEach(config => {
|
||||||
|
map[config.addressId] = [];
|
||||||
|
});
|
||||||
|
this.companies.forEach(company => {
|
||||||
|
try {
|
||||||
|
const visibleFor = JSON.parse(company.visibleForAddressId || '[]');
|
||||||
|
if (Array.isArray(visibleFor)) {
|
||||||
|
visibleFor.forEach(tenantId => {
|
||||||
|
if (map[tenantId]) map[tenantId].push(company);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const [configsRes, companiesRes] = await Promise.all([
|
||||||
|
axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderTenantConfig/getTenantConfigs`),
|
||||||
|
axios.get(`${window.TT_CONFIG.BASE_PATH}/WorkorderTenantConfig/getCompanies`),
|
||||||
|
]);
|
||||||
|
this.configs = configsRes.data;
|
||||||
|
this.companies = companiesRes.data;
|
||||||
|
} catch (e) {
|
||||||
|
window.notify('error', 'Daten konnten nicht geladen werden.');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startEdit(item) {
|
||||||
|
this.editingId = item.id;
|
||||||
|
this.editableItem = JSON.parse(JSON.stringify(item));
|
||||||
|
if (this.activeTab === 'configs') {
|
||||||
|
this.editableItem.documentationTypes = JSON.parse(this.editableItem.documentationTypes || '[]');
|
||||||
|
this.editableItem.interventionTypes = JSON.parse(this.editableItem.interventionTypes || '[]');
|
||||||
|
this.editableJsonFilter = JSON.stringify(JSON.parse(this.editableItem.workorderCreationFilters || '{}'), null, 2);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.editableItem.visibleForAddressId = JSON.parse(this.editableItem.visibleForAddressId || '[]');
|
||||||
|
} catch (e) {
|
||||||
|
this.editableItem.visibleForAddressId = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancelEdit() {
|
||||||
|
this.editingId = null;
|
||||||
|
this.editableItem = {};
|
||||||
|
this.editableJsonFilter = '';
|
||||||
|
},
|
||||||
|
async saveItem() {
|
||||||
|
const endpoint = this.activeTab === 'configs' ? 'saveTenantConfig' : 'saveCompany';
|
||||||
|
let payload = {...this.editableItem};
|
||||||
|
|
||||||
|
if (this.activeTab === 'configs') {
|
||||||
|
try {
|
||||||
|
JSON.parse(this.editableJsonFilter);
|
||||||
|
payload.workorderCreationFilters = this.editableJsonFilter;
|
||||||
|
} catch (e) {
|
||||||
|
return window.notify('error', 'Filter für Auftragserstellung ist kein valides JSON.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderTenantConfig/${endpoint}`, payload);
|
||||||
|
if (data.success) {
|
||||||
|
window.notify('success', data.message);
|
||||||
|
this.cancelEdit();
|
||||||
|
await this.fetchData();
|
||||||
|
} else {
|
||||||
|
window.notify('error', data.message || 'Speichern fehlgeschlagen.');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteItem(item, type) {
|
||||||
|
if (!confirm(`Soll der Eintrag "${item.name}" wirklich gelöscht werden?`)) return;
|
||||||
|
const endpoint = type === 'configs' ? 'deleteTenantConfig' : 'deleteCompany';
|
||||||
|
try {
|
||||||
|
const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderTenantConfig/${endpoint}`, {id: item.id});
|
||||||
|
if (data.success) {
|
||||||
|
window.notify('success', data.message);
|
||||||
|
await this.fetchData();
|
||||||
|
} else {
|
||||||
|
window.notify('error', data.message || 'Löschen fehlgeschlagen.');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openCreateModal() {
|
||||||
|
this.newItem = this.activeTab === 'configs'
|
||||||
|
? {
|
||||||
|
documentationTypes: [],
|
||||||
|
interventionTypes: [],
|
||||||
|
workorderCreationFilters: '{}',
|
||||||
|
civilEngineeringDocsRequired: 0
|
||||||
|
}
|
||||||
|
: {visibleForAddressId: []};
|
||||||
|
this.showModal = true;
|
||||||
|
},
|
||||||
|
async saveNewItem() {
|
||||||
|
const endpoint = this.activeTab === 'configs' ? 'saveTenantConfig' : 'saveCompany';
|
||||||
|
try {
|
||||||
|
const {data} = await axios.post(`${window.TT_CONFIG.BASE_PATH}/WorkorderTenantConfig/${endpoint}`, this.newItem);
|
||||||
|
if (data.success) {
|
||||||
|
window.notify('success', data.message);
|
||||||
|
this.showModal = false;
|
||||||
|
await this.fetchData();
|
||||||
|
} else {
|
||||||
|
window.notify('error', data.message || 'Speichern fehlgeschlagen.');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addTenant(tenant, isNewItem = false) {
|
||||||
|
const tenantId = typeof tenant === 'object' && tenant !== null ? tenant.value : tenant;
|
||||||
|
const targetArray = isNewItem ? this.newItem.visibleForAddressId : this.editableItem.visibleForAddressId;
|
||||||
|
if (tenantId && !targetArray.includes(tenantId)) targetArray.push(tenantId);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tenantToAdd = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeTenant(index, isNewItem = false) {
|
||||||
|
const targetArray = isNewItem ? this.newItem.visibleForAddressId : this.editableItem.visibleForAddressId;
|
||||||
|
targetArray.splice(index, 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.fetchData();
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user