Merge remote-tracking branch 'origin/spidev' into spidev
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
</style>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $openreplayUserType = 'internal'; include(__DIR__ . "/../default/includes/openreplay.php"); ?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -232,12 +232,22 @@
|
||||
<input type="text" class="form-control" name="bank_account_bic" id="bank_account_bic" value="<?=$address->bank_account_bic?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<?php if($me->can("Fibu")): ?>
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-2 col-form-label" for="bank_account_bic"></label>
|
||||
<label class="col-lg-2 col-form-label" for="manual_invoice_sepa_limit">Manuelle Rechnungen abbuchen bis (€)</label>
|
||||
<div class="col-lg-10">
|
||||
<input type="text" class="form-control" name="manual_invoice_sepa_limit" id="manual_invoice_sepa_limit" value="<?=round($address->manual_invoice_sepa_limit, 2)?>" />
|
||||
<small>Wenn Bankeinzug aktiviert ist</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-2 col-form-label" for=""></label>
|
||||
<div class="col-lg-10 alert alert-danger hidden" id="bank-error"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<?php if($me->can("Fibu")): ?>
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-2 col-form-label" for="sepa_date">Sepa Mandatsdatum</label>
|
||||
|
||||
@@ -144,8 +144,12 @@
|
||||
</tr><tr>
|
||||
<th>BIC</th>
|
||||
<td><?=$address->bank_account_bic?></td>
|
||||
</tr><tr>
|
||||
<th>Manuelle Rechnungen abbuchen bis (erfordert Bankeinzug)</th>
|
||||
<td><?=number_format($address->manual_invoice_sepa_limit, 2, ",", ".")?> €</td>
|
||||
</tr>
|
||||
<?php if($me->can("Fibu")): ?>
|
||||
|
||||
<tr>
|
||||
<th>Sepa Mandatsdatum</th>
|
||||
<td><?=($address->sepa_date) ? date("d.m.Y", $address->sepa_date) : ""?></td>
|
||||
|
||||
@@ -318,6 +318,9 @@ $pagination_entity_name = "Zu provisionierende CPEs";
|
||||
<option value="FritzBox 6490 Cable" <?= ($product->cpeprovisioning->routertype == "FritzBox 6490 Cable") ? "selected='selected'" : "" ?>>
|
||||
FritzBox 6490 Cable (Inet, Phone, IPTV)
|
||||
</option>
|
||||
<option value="FritzBox 6670 Cable" <?= ($product->cpeprovisioning->routertype == "FritzBox 6670 Cable") ? "selected='selected'" : "" ?>>
|
||||
FritzBox 6670 Cable (Inet, Phone, IPTV)
|
||||
</option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -25,22 +25,52 @@ $pagination_entity_name = "Rechnungen";
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a href="https://thetool.xinon.at/xfarm/" class="btn btn-primary" target="_blank"><i class="far fa-arrows-to-circle fa-fw"></i> Fakt-Rechnungen Import</a>
|
||||
<div class="col-3">
|
||||
<h4 class="header-title mb-3">Manuelle Rechnungen</h4>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<a href="https://thetool.xinon.at/xfarm/" class="btn btn-primary mt-4" target="_blank"><i class="far fa-arrows-to-circle fa-fw"></i> Fakt-Rechnungen Import</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<form method="post" action="<?=self::getUrl("Invoice", "manualExportBmd")?>">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<label class="form-label" for="manual_invoice_date_from">Rechnungsdatum von</label>
|
||||
<input type="text" class="form-control" name="manual_invoice_date_from" id="manual_invoice_date_from" value="" />
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label class="form-label" for="manual_invoice_date_to">Rechnungsdatum bis</label>
|
||||
<input type="text" class="form-control" name="manual_invoice_date_to" id="manual_invoice_date_to" value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-outline-primary ml-1"><i class="far fa-fw fa-file-export"></i> Rechnungsexport für BMD</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body mb-3">
|
||||
<h4 class="header-title mb-3">Filter</h4>
|
||||
<h4 class="header-title mb-3">Contract Rechnungen</h4>
|
||||
|
||||
<form method="get" action="<?=self::getUrl("Invoice")?>">
|
||||
<div class="row">
|
||||
@@ -399,6 +429,22 @@ $pagination_entity_name = "Rechnungen";
|
||||
todayBtn: 'linked',
|
||||
autoclose: true
|
||||
});
|
||||
$('#manual_invoice_date_from').datepicker({
|
||||
orientation: "bottom",
|
||||
language: 'de',
|
||||
format: "dd.mm.yyyy",
|
||||
showWeekDays: true,
|
||||
todayBtn: 'linked',
|
||||
autoclose: true
|
||||
});
|
||||
$('#manual_invoice_date_to').datepicker({
|
||||
orientation: "bottom",
|
||||
language: 'de',
|
||||
format: "dd.mm.yyyy",
|
||||
showWeekDays: true,
|
||||
todayBtn: 'linked',
|
||||
autoclose: true
|
||||
});
|
||||
$('.datepicker').datepicker({
|
||||
orientation: "bottom",
|
||||
language: 'de',
|
||||
|
||||
@@ -68,9 +68,7 @@
|
||||
<td style="vertical-align: top; text-align: right;">
|
||||
<table style="display: inline-table; vertical-align: top;">
|
||||
<tr>
|
||||
<td style="vertical-align: top; padding-right: 10px;">
|
||||
<img alt="QR-Code" src="{{ qrCodeSrc }}" style="display: block; height: 100%; max-height: 3.5cm; width: auto;">
|
||||
</td>
|
||||
{{ qrCodeHtml }}
|
||||
<td>
|
||||
<table class="invoice-details">
|
||||
<tr>
|
||||
|
||||
@@ -17,7 +17,7 @@ foreach($invoice->positions as $p) {
|
||||
}
|
||||
}
|
||||
|
||||
$gesamtrabatt = $invoice->gesamtrabatt ?? 0;
|
||||
$total_discount = $invoice->total_discount ?? 0;
|
||||
$subtotal = 0;
|
||||
foreach($invoice->positions as $p) {
|
||||
$subtotal += $p->price_total ?? 0;
|
||||
@@ -127,8 +127,8 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);
|
||||
|
||||
<h2 style="text-align: center;color: #005384">Ihre Xinon <?=($is_credit) ? "Gutschrift" : "Rechnung"?> vom <?=date("d.m.Y",$invoice->invoice_date)?></h2>
|
||||
|
||||
<?php if($invoice->einleitender_text ?? ''): ?>
|
||||
<p style="margin-top: 10pt; margin-bottom: 20pt; text-align: center; font-weight: bold;"><?=nl2br(htmlspecialchars($invoice->einleitender_text))?></p>
|
||||
<?php if($invoice->introductory_text ?? ''): ?>
|
||||
<p style="margin-top: 10pt; margin-bottom: 20pt; text-align: center; font-weight: bold;"><?=nl2br(htmlspecialchars($invoice->introductory_text))?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<table style="border-collapse: collapse; width: 100%;" id="invoiceTable">
|
||||
@@ -166,7 +166,7 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);
|
||||
|
||||
<tr class="position <?=($i%2 == 0) ? "even" : "uneven" ?>">
|
||||
<td style="padding-left: 4pt; vertical-align: top;">
|
||||
<?=htmlspecialchars($p->product_name ?? '')?>
|
||||
<?=htmlspecialchars($p->warehousearticle_name ?? '')?>
|
||||
<?php if(isset($p->product_info) && $p->product_info): ?>
|
||||
<div style="padding-left: 12pt; font-size: 10px; color: #666;"><?=htmlspecialchars($p->product_info)?></div>
|
||||
<?php endif; ?>
|
||||
@@ -186,17 +186,17 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);
|
||||
endforeach;
|
||||
endforeach;
|
||||
?>
|
||||
<?php if($gesamtrabatt > 0): ?>
|
||||
<?php if($total_discount > 0): ?>
|
||||
<tr style="background-color: #ebebeb; border-top: 2px solid black;">
|
||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Zwischensumme:</td>
|
||||
<td colspan="2" style="text-align: right; padding-right: 4pt;"><?=number_format($subtotal, 2, ",","."). " €"?></td>
|
||||
</tr>
|
||||
<tr style="background-color: #ebebeb; border-bottom: 1px solid #ccc;">
|
||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamtrabatt <?=number_format($gesamtrabatt, 2, ",", ".")?>%:</td>
|
||||
<td colspan="2" style="text-align: right; padding-right: 4pt; color: #d32f2f;">-<?=number_format($subtotal * ($gesamtrabatt / 100), 2, ",","."). " €"?></td>
|
||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamtrabatt <?=number_format($total_discount, 2, ",", ".")?>%:</td>
|
||||
<td colspan="2" style="text-align: right; padding-right: 4pt; color: #d32f2f;">-<?=number_format($subtotal * ($total_discount / 100), 2, ",","."). " €"?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr style="font-weight: bold; background-color: #ebebeb; border-bottom: 1px solid black;<?=($gesamtrabatt > 0) ? '' : 'border-top: 2px solid black;'?>">
|
||||
<tr style="font-weight: bold; background-color: #ebebeb; border-bottom: 1px solid black;<?=($total_discount > 0) ? '' : 'border-top: 2px solid black;'?>">
|
||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamt Netto:</td>
|
||||
<td colspan="2" style="text-align: right; padding-right: 4pt;"><?=number_format($net_total, 2, ",","."). " €"?></td>
|
||||
</tr>
|
||||
|
||||
9
Layout/default/MobileApp/App.php
Normal file
9
Layout/default/MobileApp/App.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
$appConfig = [
|
||||
'title' => 'Xinon Mobile',
|
||||
'appName' => 'Xinon',
|
||||
'manifestPath' => '/mobile/manifest.json',
|
||||
'appJsPath' => '/mobile/app.js',
|
||||
'swPath' => '/mobile/sw.js',
|
||||
];
|
||||
require __DIR__ . '/Base.php';
|
||||
71
Layout/default/MobileApp/Base.php
Normal file
71
Layout/default/MobileApp/Base.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
$config = array_merge([
|
||||
'title' => 'Xinon Mobile',
|
||||
'appName' => 'Xinon',
|
||||
'manifestPath' => '/mobile/manifest.json',
|
||||
'appJsPath' => '/mobile/app.js',
|
||||
'swPath' => '/mobile/sw.js',
|
||||
'additionalStylesheets' => [],
|
||||
], $appConfig ?? []);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title><?= htmlspecialchars($config['title']) ?></title>
|
||||
<link rel="shortcut icon" href="/assets/images/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="/assets/images/xinon-sm-192.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/assets/images/xinon-sm-192.png">
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="/assets/images/xinon-sm-512.png">
|
||||
<link rel="manifest" href="<?= htmlspecialchars($config['manifestPath']) ?>">
|
||||
<meta name="theme-color" content="#005384">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="<?= htmlspecialchars($config['appName']) ?>">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3.4.27/dist/vue.global.prod.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios@1.7.2/dist/axios.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>
|
||||
<link rel="stylesheet" href="/mobile/shared/base.css">
|
||||
<?php foreach ($config['additionalStylesheets'] as $sheet): ?>
|
||||
<link rel="stylesheet" href="<?= htmlspecialchars($sheet) ?>">
|
||||
<?php endforeach; ?>
|
||||
<script>
|
||||
window.TT_CONFIG = <?= json_encode($JSGlobals ?? []) ?>;
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'primary': '#005384',
|
||||
'secondary': '#fac41b',
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body class="transition-colors duration-300 overflow-hidden">
|
||||
<div id="app" class="h-screen w-screen overflow-hidden antialiased">
|
||||
<div class="flex items-center justify-center h-full bg-slate-100 dark:bg-slate-900">
|
||||
<div class="text-center">
|
||||
<img src="/assets/images/xinon-full-transparent.png" class="h-12 mx-auto mb-4 dark:hidden">
|
||||
<img src="/assets/images/xinon-full-transparent-white.png" class="h-12 mx-auto mb-4 hidden dark:block">
|
||||
<div class="animate-pulse text-slate-500 dark:text-slate-400">Lädt...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="<?= htmlspecialchars($config['appJsPath']) ?>"></script>
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('<?= htmlspecialchars($config['swPath']) ?>')
|
||||
.then(reg => console.log('SW registered:', reg.scope))
|
||||
.catch(err => console.log('SW registration failed:', err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
10
Layout/default/MobileApp/WarehouseStocktake.php
Normal file
10
Layout/default/MobileApp/WarehouseStocktake.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
$appConfig = [
|
||||
'title' => 'Lager Inventur',
|
||||
'appName' => 'Inventur',
|
||||
'manifestPath' => '/mobile/warehouse-stocktake/manifest.json',
|
||||
'appJsPath' => '/mobile/warehouse-stocktake/app.js',
|
||||
'swPath' => '/mobile/warehouse-stocktake/sw.js',
|
||||
'additionalStylesheets' => ['/mobile/warehouse-stocktake/app.css'],
|
||||
];
|
||||
require __DIR__ . '/Base.php';
|
||||
@@ -437,7 +437,7 @@
|
||||
<div class="form-group row">
|
||||
<label class="col-lg-2 col-form-label" for="order_date">Bestelldatum</label>
|
||||
<div class="col-lg-4">
|
||||
<input type="text" class="form-control" name="order_date" id="order_date" value="<?=($order->order_date) ? date("d.m.Y", $order->order_date) : ""?>" />
|
||||
<input type="text" class="form-control" name="order_date" id="order_date" value="<?=(is_numeric($order->order_date)) ? date("d.m.Y", $order->order_date) : $order->order_date ?>" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
@@ -553,7 +553,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body" id="products-form">
|
||||
<h4>Produkte</h4>
|
||||
@@ -585,6 +584,9 @@
|
||||
<div class="row product-container" id="position-<?=$i?>">
|
||||
<input type="hidden" name="products[<?=$i?>][delete]" id="products-<?=$i?>-delete" value="0" />
|
||||
<input type="hidden" name="products[<?=$i?>][orderproduct_id]" value="<?=$product->id?>" />
|
||||
<?php if($product->preorder_id): ?>
|
||||
<input type="hidden" name="products[<?=$i?>][preorder_id]" value="<?=$product->preorder_id?>" />
|
||||
<?php endif; ?>
|
||||
<div class="col-md-1 product-<?=$i?>">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@@ -596,9 +598,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 delete-button-container">
|
||||
<i class="btn btn-outline-danger fas fa-trash-can pointer" style="font-size: 1.5em" onclick="toggleDeletePos(<?=$i?>)"></i>
|
||||
</div>
|
||||
<?php if(!$product->preorder_id): ?>
|
||||
<div class="col-md-12 delete-button-container">
|
||||
<a href="#" class="btn btn-xl btn-outline-danger" onclick="toggleDeletePos(<?=$i?>); return false;"><i class="fas fa-fw fa-trash-can fa-xl pointer"></i></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -641,7 +645,17 @@
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<?php if($product->product->getAttributeValue("oaid_enabled")): ?>
|
||||
<div class="row mt-1 mb-2" id="oaid-<?=$i?>-line">
|
||||
<?php else: ?>
|
||||
<div class="row mt-1 mb-2 hidden" id="oaid-<?=$i?>-line">
|
||||
<?php endif; ?>
|
||||
<div class="col-4">
|
||||
<label class="form-label" for="oaid-<?=$i?>">OAID</label>
|
||||
<input type="text" name="products[<?=$i?>][oaid]" id="oaid-<?=$i?>" class="form-control" value="<?=$product->oaid?>" placeholder="optional">
|
||||
</div>
|
||||
</div>
|
||||
<?php if(
|
||||
(is_array($product->product->attributes) && count($product->product->attributes))
|
||||
&& (array_key_exists(TT_ATTRIB_TERMINATION_REQUIRED_NAME, $product->product->attributes)
|
||||
@@ -828,7 +842,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 delete-button-container">
|
||||
<i class="btn btn-outline-info fas fa-trash-can pointer" title="Inhalte löschen" style="font-size: 1.5em" onclick="clearNewPos(<?=$i?>)"></i>
|
||||
<a href="#" class="btn btn-xl btn-outline-info" title="Inhalte löschen" onclick="clearNewPos(<?=$i?>); return false;"><i class="fas fa-fw fa-trash-can fa-xl pointer"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -869,7 +883,14 @@
|
||||
<input type="text" class="form-control" name="products[<?=$i?>][price_setup]" id="price_setup-<?=$i?>" value="" placeholder="Preis Setup" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row mt-1 mb-2 hidden" id="oaid-<?=$i?>-line">
|
||||
<div class="col-4">
|
||||
<label class="form-label" for="oaid-<?=$i?>">OAID</label>
|
||||
<input type="text" name="products[<?=$i?>][oaid]" id="oaid-<?=$i?>" class="form-control" value="<?=$product->oaid?>" placeholder="optional">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-1 mb-2 hidden" id="termination_id-<?=$i?>-line">
|
||||
<!-- line to choose termination -->
|
||||
<div class="col-12">
|
||||
@@ -1383,8 +1404,15 @@
|
||||
});
|
||||
} else {
|
||||
$('#termination_id-' + id + '-line').hide();
|
||||
//$('#termination_id-' + id + '-line').hide();
|
||||
}
|
||||
|
||||
if(typeof p.attributes === 'object' && "oaid_enabled" in p.attributes && p.attributes.oaid_enabled == 1) {
|
||||
console.log("oaid_enabled");
|
||||
$('#oaid-' + id + '-line').show();
|
||||
console.log($('#oaid-' + id).val());
|
||||
} else {
|
||||
$('#oaid-' + id + '-line').hide();
|
||||
}
|
||||
|
||||
if(typeof p.attributes === 'object' && "needs_number" in p.attributes && p.attributes.needs_number == 1) {
|
||||
console.log("needs_number");
|
||||
@@ -1893,7 +1921,7 @@
|
||||
} else {
|
||||
$('#products-' + id + '-delete').val(0);
|
||||
//$('#position-' + id + ' .delete-button-container i').removeClass("fa-trash-can-slash").addClass("fa-trash-can");
|
||||
$('#position-' + id + ' .delete-button-container').html('<i class="btn btn-outline-danger fas fa-trash-can pointer" style="font-size: 1.5em" onclick="toggleDeletePos(' + id + ')"></i>');
|
||||
$('#position-' + id + ' .delete-button-container').html('<a href="#" class="btn btn-xl btn-outline-info" title="Inhalte löschen" onclick="toggleDeletePos(' + id + '); return false;"><i class="fas fa-fw fa-trash-can fa-xl pointer"></i></a>');
|
||||
//$('#position-' + id + ' .delete-button-container i').removeClass("btn-outline-white fa-trash-can-slash").addClass("text-danger fa-trash-can");
|
||||
$('#position-' + id).removeClass('text-white deleted');
|
||||
|
||||
@@ -1932,7 +1960,7 @@
|
||||
</div> \
|
||||
<div class="row"> \
|
||||
<div class="col-md-12 delete-button-container"> \
|
||||
<i class="btn btn-outline-info fas fa-trash-can pointer" title="Inhalte löschen" style="font-size: 1.5em" onclick="clearNewPos(' + i +')"></i> \
|
||||
<a href="#" class="btn btn-xl btn-outline-info" title="Inhalte löschen" onclick="clearNewPos(' + i +'); return false;"><i class="fas fa-fw fa-trash-can fa-xl pointer"></i></a> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div> \
|
||||
@@ -1973,7 +2001,14 @@
|
||||
<input type="text" class="form-control" name="products[' + i +'][price_setup]" id="price_setup-' + i +'" value="" placeholder="Preis Setup" /> \
|
||||
</div> \
|
||||
</div> \
|
||||
\
|
||||
\
|
||||
<div class="row mt-1 mb-2 hidden" id="oaid-<?=$i?>-line"> \
|
||||
<div class="col-4"> \
|
||||
<label class="form-label" for="oaid-<?=$i?>">OAID</label> \
|
||||
<input type="text" name="products[<?=$i?>][oaid]" id="oaid-<?=$i?>" class="form-control" value="" placeholder="optional"> \
|
||||
</div> \
|
||||
</div> \
|
||||
\
|
||||
<div class="row mt-1 mb-2 hidden" id="termination_id-' + i +'-line"> \
|
||||
<!-- line to choose termination --> \
|
||||
<div class="col-12"> \
|
||||
|
||||
@@ -299,6 +299,10 @@
|
||||
<a href="<?=self::getUrl("Order", "sendServicePin", ["id" => $order->id])?>" onclick="if(!confirm('Soll der Service-PIN an den Vertragsinhaber gesendet werden?')) return false;"><i class="fas fa-paper-plane" title="Service PIN als PDF per Email an Vertragsinhaber"></i></a>
|
||||
<a href="<?=self::getUrl("Order", "edit", ["id" => $order->id, "filter" => $filter, "noTermProducts" => 1])?>"><i class="far fa-edit" title="Bearbeiten"></i></a>
|
||||
<a href="<?=self::getUrl("Order", "delete", ["id" => $order->id])?>" onclick="if(!confirm('Bestellung wirklich löschen?')) return false;" class="text-danger" title="Löschen"><i class="fas fa-trash"></i></a>
|
||||
|
||||
<?php if(!$order->getSnoppProduct() && ($order->getPreorderProduct() || $order->getOaidProduct())): ?>
|
||||
<a href="<?=self::getUrl("Order", "createSnoppOrder", ["id" => $order->id, "filter" => $filter, "noTermProducts" => 1])?>" class="ml-2"><img src="<?=self::getResourcePath()?>img/snop-logo.png" style="width:24px;height:auto;" /></a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="building-list-tr <?=($order_count % 2 == 0) ? "table-bg-even" : ""?>" id="order-dates-<?=$order->id?>">
|
||||
@@ -543,7 +547,10 @@
|
||||
<td class="text-right"><?=$product->pos?></td>
|
||||
<td class="text-right"><?=$product->formatAmount()?></td>
|
||||
<td>
|
||||
<?=$product->product->name?>
|
||||
<?php if($product->snopp_order_id): ?>
|
||||
<img src="<?=self::getResourcePath()?>/img/snop-logo.png" style="width:24px;height:auto;" title="Bestellung in Snopp">
|
||||
<?php endif; ?>
|
||||
<?=$product->product->name?> <?=$product->oaid ? "<span class='text-pink font-italic'>{$product->oaid}</span>" : ""?>
|
||||
<?php
|
||||
if(
|
||||
(is_array($product->product->attributes) && count($product->product->attributes))
|
||||
|
||||
@@ -888,9 +888,10 @@ $pagination_entity_name = "Vorbestellungen";
|
||||
Filter-Vorlagen <i class="fas fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["status" => [21,22,23,24,25], "rimo_workorder" => 1, "borderpoint" => "all"]])?>">Gelöschte Bestellungen mit Workorder</a></li>
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["status" => [21,22,23,24,25,930,931,932,933,934], "rimo_workorder" => 1, "rimo_workorder_status" => ["Clarify","Accepted","Plan released","Assigned","Executed","Documented","Review"]]])?>">Gelöschte Bestellungen mit Workorder</a></li>
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["preorder_status_flags" => [4], "connection_type" => ["apartment", "apartment-building"], "borderpoint" => "all"]])?>">Wohnung - Verkabelung erledigt</a></li>
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["status" => [21,22,23,24,25]]])?>">Storniert</a></li>
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["status" => [1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,18,28,34]]])?>">Offene Bestellungen Status 10-299</a></li>
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["status" => [21,22,23,24,25,29,30,31,32,33]]])?>">Storniert</a></li>
|
||||
<?php if ($me->isAdmin() || $me->address->id == 209): ?>
|
||||
<li><a class="dropdown-item" href="<?=self::getUrl("Preorder", "Index", ["filter" => ["onlyShowCustomMailSent" => 1]])?>">Bestellungen mit gesendeter Custom-300 Benachrichtigung</a></li>
|
||||
<input type="hidden" name="filter[onlyShowCustomMailSent]" value="<?= (isset($filter['onlyShowCustomMailSent']) && $filter['onlyShowCustomMailSent'] == 1) ? 1 : 0 ?>"/>
|
||||
@@ -1359,7 +1360,7 @@ $pagination_entity_name = "Vorbestellungen";
|
||||
* Globals for map display
|
||||
*/
|
||||
var borderpolies = [];
|
||||
<?php if($me->is("Admin")): ?>
|
||||
<?php if($me->is("Admin") && !isset($campaign)): ?>
|
||||
<?php foreach(ADBNetzgebietModel::search(["borderpoly" => true]) as $bp_netz): ?>
|
||||
borderpolies.push([<?=$bp_netz->borderpoly?>]);
|
||||
<?php endforeach; ?>
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="card-header bg-info text-white pl-2 pr-2 pt-1 pb-1">Details</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="loader-big hidden" ><img src="<?=self::getResourcePath()?>assets/images/loader-big.gif" /></div>
|
||||
@@ -170,6 +172,18 @@
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<div class="row mb-2">
|
||||
<div class="col-8">
|
||||
<?php if($preorder->orderproduct): ?>
|
||||
<a href="<?=self::getUrl("Order", "index", ["id" => $preorder->orderproduct->order_id])?>" target="_blank"><i class="far fa-fw fa-angle-double-right"></i> Internetbestellung anzeigen</a>
|
||||
<?php else: ?>
|
||||
<a href="<?=self::getUrl("Preorder","createOrderFromPreorder", ["preorder_id" => $preorder->id])?>" target="_blank" class="btn btn-outline-primary"><i class="far fa-fw fa-angle-double-right"></i> Internetbestellung anlegen</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-4">
|
||||
|
||||
@@ -697,6 +711,7 @@
|
||||
<th>ctag</th>
|
||||
<th>Typ</th>
|
||||
<th>External ID</th>
|
||||
<th>External Name</th>
|
||||
<th>External State</th>
|
||||
</tr>
|
||||
<?php if(is_array($preorder->ctags) && count($preorder->ctags)): ?>
|
||||
@@ -706,6 +721,7 @@
|
||||
<td class="text-monospace"><?=$ctag->ctag?></td>
|
||||
<td class="text-monospace"><?=$ctag->service_type?></td>
|
||||
<td class="text-monospace"><?=htmlentities($ctag->ext_id)?></td>
|
||||
<td class="text-monospace"><?=htmlentities($ctag->ext_name)?></td>
|
||||
<td class="text-monospace"><?=htmlentities($ctag->ext_status)?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
<option></option>
|
||||
<option value="ported_out" <?=($number->disabled_reason == "ported_out") ? "selected='selected'" : ""?>>Zu neuem Provider portiert</option>
|
||||
<option value="ported_back" <?=($number->disabled_reason == "ported_back") ? "selected='selected'" : ""?>>Zum Anker zurückportiert</option>
|
||||
<option value="contract_cancelled" <?=($number->disabled_reason == "contract_cancelled") ? "selected='selected'" : ""?>>Vertrag gekündigt</option>
|
||||
<option value="reserved" <?=($number->disabled_reason == "reserved") ? "selected='selected'" : ""?>>Reserviert</option>
|
||||
<option value="legacy" <?=($number->disabled_reason == "legacy") ? "selected='selected'" : ""?>>Legacy</option>
|
||||
<option value="damaged" <?=($number->disabled_reason == "damaged") ? "selected='selected'" : ""?>>Kaputt</option>
|
||||
|
||||
@@ -17,14 +17,16 @@
|
||||
</tr>
|
||||
<?php $i = 0; foreach(range((array_key_exists($block->id, $num_from) ? $num_from[$block->id] : $block->first), $block->last) as $number): ?>
|
||||
<?php $num = VoicenumberModel::getFirst(['voicenumberblock_id' => $block->id, 'number' => $number]) ?>
|
||||
<tr>
|
||||
<tr class="<?=($num->disabled) ? "bg-secondary text-white" : ""?>">
|
||||
<td><?=$number?></td>
|
||||
<td>
|
||||
<?php if($num->active): ?>
|
||||
<span class="text-success"><i class="fas fa-check"></i></span>
|
||||
<small class="text-monospace">(seit <?=($num->id) ? date("d.m.Y H:i:s", $num->activated_date) : ""?>)</small>
|
||||
<span class="text-success"><i class="fas fa-check"></i></span>
|
||||
<small class="text-monospace">(seit <?=($num->id) ? date("d.m.Y H:i:s", $num->activated_date) : ""?>)</small>
|
||||
<?php elseif($num->disabled): ?>
|
||||
<span class="text-success text-danger" title="Nummer wurde wegportiert"><i class="far fa-anchor bg-white" style="padding: 2px;"></i> <i class="far fa-file-export bg-white" style="padding: 2px;"></i></span>
|
||||
<?php else: ?>
|
||||
<span class="text-danger"><i class="fas fa-times"></i></span>
|
||||
<span class="text-danger"><i class="fas fa-times"></i></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><a href="<?=self::getUrl("Contract", "view", ["id" => $num->contract_id])?>"><?=$num->contract_id?></a></td>
|
||||
@@ -40,7 +42,7 @@
|
||||
Lokal
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?=$num->disabled_reason?></td>
|
||||
<td><?=$num->disabled_reason?><?=($num->disabled > 1) ? " (<i>".date("d.m.Y H:i", $num->disabled)."</i>)" : ""?></td>
|
||||
<td><?=($num->id && $num->enable_on_date) ? date("d.m.Y", $num->enable_on_date) : ""?></td>
|
||||
<td>
|
||||
<a href="<?=self::getUrl("Voicenumber", "edit", ["block_id" => $block->id, "number" => $number])?>"><i class="fas fa-edit"></i></a>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<?php
|
||||
// Prepare OpenReplay user data
|
||||
$openreplayUserId = '';
|
||||
if (class_exists('mfUser') && class_exists('mfLoginController') && mfLoginController::isLoggedIn()) {
|
||||
$user = mfUser::singleton();
|
||||
if ($user && $user->id) {
|
||||
$openreplayUserId = (string) $user->id;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
@@ -32,6 +40,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- OpenReplay Session Recording -->
|
||||
<script>
|
||||
var initOpts = {
|
||||
projectKey: "96MdXVcId8Ph3eOirMWj",
|
||||
ingestPoint: "https://openreplay.xinon.at/ingest",
|
||||
defaultInputMode: 2,
|
||||
obscureTextNumbers: false,
|
||||
obscureTextEmails: true,
|
||||
};
|
||||
var startOpts = { userID: <?= json_encode($openreplayUserId) ?> };
|
||||
(function(A,s,a,y,e,r){
|
||||
r=window.OpenReplay=[e,r,y,[s-1, e]];
|
||||
s=document.createElement('script');s.src=A;s.async=!a;
|
||||
document.getElementsByTagName('head')[0].appendChild(s);
|
||||
r.start=function(v){r.push([0])};
|
||||
r.stop=function(v){r.push([1])};
|
||||
r.setUserID=function(id){r.push([2,id])};
|
||||
r.setUserAnonymousID=function(id){r.push([3,id])};
|
||||
r.setMetadata=function(k,v){r.push([4,k,v])};
|
||||
r.event=function(k,p,i){r.push([5,k,p,i])};
|
||||
r.issue=function(k,p){r.push([6,k,p])};
|
||||
r.isActive=function(){return false};
|
||||
r.getSessionToken=function(){};
|
||||
})("//static.openreplay.com/17.0.0/openreplay.js",1,0,initOpts,startOpts);
|
||||
window.OpenReplay.setMetadata('userType', 'internal');
|
||||
window.OpenReplay.setMetadata('app', 'warehouse-stocktake-pwa');
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
overscroll-behavior: none;
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<?php
|
||||
// Prepare OpenReplay user data for external company users
|
||||
$openreplayUserId = '';
|
||||
$openreplayCompanyId = $JSGlobals['COMPANY_ID'] ?? '';
|
||||
if (class_exists('mfUser') && class_exists('mfLoginController') && mfLoginController::isLoggedIn()) {
|
||||
$user = mfUser::singleton();
|
||||
if ($user && $user->id) {
|
||||
$openreplayUserId = 'company_' . $user->id;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
@@ -34,6 +43,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- OpenReplay Session Recording -->
|
||||
<script>
|
||||
var initOpts = {
|
||||
projectKey: "96MdXVcId8Ph3eOirMWj",
|
||||
ingestPoint: "https://openreplay.xinon.at/ingest",
|
||||
defaultInputMode: 2,
|
||||
obscureTextNumbers: false,
|
||||
obscureTextEmails: true,
|
||||
};
|
||||
var startOpts = { userID: <?= json_encode($openreplayUserId) ?> };
|
||||
(function(A,s,a,y,e,r){
|
||||
r=window.OpenReplay=[e,r,y,[s-1, e]];
|
||||
s=document.createElement('script');s.src=A;s.async=!a;
|
||||
document.getElementsByTagName('head')[0].appendChild(s);
|
||||
r.start=function(v){r.push([0])};
|
||||
r.stop=function(v){r.push([1])};
|
||||
r.setUserID=function(id){r.push([2,id])};
|
||||
r.setUserAnonymousID=function(id){r.push([3,id])};
|
||||
r.setMetadata=function(k,v){r.push([4,k,v])};
|
||||
r.event=function(k,p,i){r.push([5,k,p,i])};
|
||||
r.issue=function(k,p){r.push([6,k,p])};
|
||||
r.isActive=function(){return false};
|
||||
r.getSessionToken=function(){};
|
||||
})("//static.openreplay.com/17.0.0/openreplay.js",1,0,initOpts,startOpts);
|
||||
window.OpenReplay.setMetadata('userType', 'external');
|
||||
window.OpenReplay.setMetadata('companyId', <?= json_encode($openreplayCompanyId) ?>);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
/* Prevents the rubber-band scroll effect on iOS and pull-to-refresh on Android */
|
||||
@@ -247,6 +284,14 @@
|
||||
return documentation.journals.filter(j => !j.text.toLowerCase().includes('wurde zugewiesen.'));
|
||||
});
|
||||
|
||||
const isCivilEngineering = computed(() => {
|
||||
return selectedWorkorder.value?.status === 'civil_engineering_required';
|
||||
});
|
||||
|
||||
const showNormalDocsForCivilEng = computed(() => {
|
||||
return isCivilEngineering.value && tenantConfig.value?.tiefbauSeesNormalDocs;
|
||||
});
|
||||
|
||||
|
||||
// --- METHODS ---
|
||||
const applyTheme = () => {
|
||||
@@ -485,6 +530,22 @@
|
||||
}
|
||||
};
|
||||
|
||||
const completeCivilEngineering = async () => {
|
||||
if (!confirm("Möchten Sie den Tiefbau wirklich abschließen?")) return;
|
||||
try {
|
||||
const response = await api.post('/completeCivilEngineering', { workorderId: selectedWorkorder.value.id });
|
||||
if (response.data.success) {
|
||||
await fetchWorkorders();
|
||||
closeDetails();
|
||||
} else {
|
||||
alert(response.data.message);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("Failed to complete civil engineering", e);
|
||||
alert(e.response?.data?.message || 'Fehler beim Abschließen des Tiefbaus.');
|
||||
}
|
||||
};
|
||||
|
||||
const selectFcp = (fcpValue) => {
|
||||
selectedFcp.value = fcpValue;
|
||||
isFcpSelectOpen.value = false;
|
||||
@@ -520,10 +581,11 @@
|
||||
checklist, fullscreenViewer, missingTasksPopover, translatedDocs, filteredJournals, installModal, isStandalone,
|
||||
selectedFcp, isFcpSelectOpen, fcpOptions, selectedFcpText, fcpSearchTerm, filteredFcpOptions, fcpInputRef,
|
||||
isSettingsOpen, theme, showThemePicker,
|
||||
savingData, // <-- ADDED
|
||||
savingData,
|
||||
isCivilEngineering, showNormalDocsForCivilEng,
|
||||
fetchWorkorders, openDetails, closeDetails, getStatusInfo, formatDate, googleMapsLink, startEditInfo, saveAdditionalInfo,
|
||||
handleFileSelect, executeUpload, addJournalEntry, submitProblem, handleCompleteClick, selectFcp, setTheme,
|
||||
saveWorkorderData // <-- ADDED
|
||||
saveWorkorderData, completeCivilEngineering
|
||||
};
|
||||
},
|
||||
template: `
|
||||
@@ -673,7 +735,7 @@
|
||||
{{ savingData ? 'Speichert...' : 'Daten speichern' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-slate-900 p-4 rounded-lg border border-slate-200 dark:border-slate-800">
|
||||
<div v-if="!isCivilEngineering || showNormalDocsForCivilEng" class="bg-white dark:bg-slate-900 p-4 rounded-lg border border-slate-200 dark:border-slate-800">
|
||||
<h3 class="font-bold text-slate-700 dark:text-secondary mb-3">Checkliste</h3>
|
||||
<div v-if="isDetailsLoading" class="space-y-3 animate-pulse">
|
||||
<div v-for="i in 4" :key="i" class="flex items-center">
|
||||
@@ -693,7 +755,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-slate-900 p-4 rounded-lg border border-slate-200 dark:border-slate-800">
|
||||
<div v-if="!isCivilEngineering || showNormalDocsForCivilEng" class="bg-white dark:bg-slate-900 p-4 rounded-lg border border-slate-200 dark:border-slate-800">
|
||||
<h3 class="font-bold text-slate-700 dark:text-secondary mb-2">Dokumentation</h3>
|
||||
<label for="file-upload" class="w-full inline-flex items-center justify-center px-4 py-2 border border-dashed border-slate-300 dark:border-slate-700 text-sm font-medium rounded-md text-slate-700 dark:text-slate-200 bg-slate-50 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-700 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" /></svg>
|
||||
@@ -718,7 +780,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white dark:bg-slate-900 p-4 rounded-lg border border-slate-200 dark:border-slate-800">
|
||||
<div v-if="!isCivilEngineering || showNormalDocsForCivilEng" class="bg-white dark:bg-slate-900 p-4 rounded-lg border border-slate-200 dark:border-slate-800">
|
||||
<h3 class="font-bold text-slate-700 dark:text-secondary mb-4">Journal</h3>
|
||||
<div v-if="isDetailsLoading" class="animate-pulse">
|
||||
<div class="flex items-start">
|
||||
@@ -748,18 +810,23 @@
|
||||
|
||||
<footer class="bg-white dark:bg-slate-900 p-2 border-t border-slate-200 dark:border-slate-800 flex-shrink-0 grid grid-cols-2 gap-2 pt-2 px-2 pb-[calc(0.5rem+env(safe-area-inset-bottom))]">
|
||||
<button @click="problemModal.show = true" class="w-full px-4 py-3 bg-red-600 text-white font-bold rounded-md text-center">Problem melden</button>
|
||||
<div class="relative">
|
||||
<button @click="handleCompleteClick" class="w-full px-4 py-3 bg-green-600 text-white font-bold rounded-md text-center disabled:bg-slate-300">Abschließen</button>
|
||||
<transition name="fade">
|
||||
<div v-if="missingTasksPopover.show" class="absolute bottom-full right-0 mb-2 w-72 bg-red-700 text-white text-sm rounded-lg shadow-lg p-3">
|
||||
<h4 class="font-bold mb-1">Fehlende Punkte:</h4>
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
<li v-for="task in missingTasksPopover.tasks" :key="task">{{ task }}</li>
|
||||
</ul>
|
||||
<div class="absolute bottom-[-5px] right-[calc(6rem-8px)] w-0 h-0 border-x-8 border-x-transparent border-t-8 border-t-red-700"></div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<template v-if="isCivilEngineering">
|
||||
<button @click="completeCivilEngineering" class="w-full px-4 py-3 bg-green-600 text-white font-bold rounded-md text-center">Tiefbau abschließen</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="relative">
|
||||
<button @click="handleCompleteClick" class="w-full px-4 py-3 bg-green-600 text-white font-bold rounded-md text-center disabled:bg-slate-300">Abschließen</button>
|
||||
<transition name="fade">
|
||||
<div v-if="missingTasksPopover.show" class="absolute bottom-full right-0 mb-2 w-72 bg-red-700 text-white text-sm rounded-lg shadow-lg p-3">
|
||||
<h4 class="font-bold mb-1">Fehlende Punkte:</h4>
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
<li v-for="task in missingTasksPopover.tasks" :key="task">{{ task }}</li>
|
||||
</ul>
|
||||
<div class="absolute bottom-[-5px] right-[calc(6rem-8px)] w-0 h-0 border-x-8 border-x-transparent border-t-8 border-t-red-700"></div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
</footer>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
@@ -18,21 +18,21 @@ $qrCodeBase64 = (new QRCode($options))->render($qrData);
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; }
|
||||
html, body { height: 25mm; width: 50mm; }
|
||||
html, body { height: 25mm; width: 63mm; overflow: hidden; }
|
||||
body { font-family: Arial, sans-serif; color: #000; }
|
||||
table { border-collapse: collapse; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table cellpadding="0" cellspacing="0" border="0" style="width: 50mm; height: 25mm;">
|
||||
<table cellpadding="0" cellspacing="0" border="0" style="width: 63mm; height: 25mm;">
|
||||
<tr>
|
||||
<td style="width: 22mm; height: 25mm; vertical-align: middle; text-align: center;">
|
||||
<img src="<?php echo $qrCodeBase64; ?>" style="width: 21mm; height: 21mm;">
|
||||
<td style="width: 24mm; height: 25mm; position: relative;">
|
||||
<img src="<?php echo $qrCodeBase64; ?>" style="width: 21mm; height: 21mm; position: absolute; top: 50%; left: 50%; margin-top: -10.5mm; margin-left: -10.5mm;">
|
||||
</td>
|
||||
<td style="height: 25mm; vertical-align: middle; padding-left: 1mm; padding-right: 1mm;">
|
||||
<img src="<?php echo BASEDIR; ?>/public/assets/images/xinon-full-transparent.png" style="width: 24mm; height: auto; display: block; margin-bottom: 1mm;">
|
||||
<div style="font-size: 10px; font-weight: bold; color: #000; text-align: center;"><?php echo htmlspecialchars($articleNumber); ?></div>
|
||||
<div style="font-size: 7px; color: #000; margin-top: 1px; word-wrap: break-word; overflow: hidden; text-align: center;"><?php echo htmlspecialchars($articleTitle); ?></div>
|
||||
<img src="<?php echo BASEDIR; ?>/public/assets/images/xinon-full-transparent.png" style="width: 24mm; height: auto; display: block; margin: 0 auto 1mm auto;">
|
||||
<div style="font-size: 11px; font-weight: bold; color: #000; text-align: center;"><?php echo htmlspecialchars($articleNumber); ?></div>
|
||||
<div style="font-size: 9px; color: #000; margin-top: 1px; word-wrap: break-word; overflow: hidden; text-align: center;"><?php echo htmlspecialchars($articleTitle); ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
54
Layout/default/WarehouseArticle/LABEL_BULK.php
Normal file
54
Layout/default/WarehouseArticle/LABEL_BULK.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
|
||||
// QR code options - small padding, high quality
|
||||
$options = new QROptions([
|
||||
'outputType' => QRCode::OUTPUT_IMAGE_PNG,
|
||||
'scale' => 10,
|
||||
'quietzoneSize' => 1,
|
||||
]);
|
||||
$qrcode = new QRCode($options);
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body { font-family: Arial, sans-serif; color: #000; }
|
||||
table { border-collapse: collapse; }
|
||||
.label-page {
|
||||
height: 25mm;
|
||||
width: 63mm;
|
||||
overflow: hidden;
|
||||
page-break-after: always;
|
||||
}
|
||||
/* Last page should not have a break if possible, but wkhtmltopdf handles it fine usually */
|
||||
.label-page:last-child {
|
||||
page-break-after: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php foreach($articles as $article):
|
||||
$qrData = "WA:" . $article->id . ":" . $article->articleNumber;
|
||||
$qrCodeBase64 = $qrcode->render($qrData);
|
||||
?>
|
||||
<div class="label-page">
|
||||
<table cellpadding="0" cellspacing="0" border="0" style="width: 63mm; height: 25mm;">
|
||||
<tr>
|
||||
<td style="width: 24mm; height: 25mm; position: relative;">
|
||||
<img src="<?php echo $qrCodeBase64; ?>" style="width: 21mm; height: 21mm; position: absolute; top: 50%; left: 50%; margin-top: -10.5mm; margin-left: -10.5mm;">
|
||||
</td>
|
||||
<td style="height: 25mm; vertical-align: middle; padding-left: 1mm; padding-right: 1mm;">
|
||||
<img src="<?php echo BASEDIR; ?>/public/assets/images/xinon-full-transparent.png" style="width: 24mm; height: auto; display: block; margin: 0 auto 1mm auto;">
|
||||
<div style="font-size: 11px; font-weight: bold; color: #000; text-align: center;"><?php echo htmlspecialchars($article->articleNumber); ?></div>
|
||||
<div style="font-size: 9px; color: #000; margin-top: 1px; word-wrap: break-word; overflow: hidden; text-align: center;"><?php echo htmlspecialchars($article->title); ?></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -61,8 +61,10 @@ if ($includeTax) {
|
||||
}
|
||||
|
||||
$formattedOfferDate = date("d.m.Y", $offerDate);
|
||||
$validityDays = isset($validity) ? (int)$validity : 14;
|
||||
$formattedValidUntil = date("d.m.Y", strtotime("+$validityDays days", $offerDate));
|
||||
$validityDays = isset($validity) ? (int)$validity : 31;
|
||||
// Use versionDate (when this version was created) for validity calculation, fallback to offerDate
|
||||
$validityBaseDate = isset($versionDate) ? $versionDate : $offerDate;
|
||||
$formattedValidUntil = date("d.m.Y", strtotime("+$validityDays days", $validityBaseDate));
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
.invoice-details td {
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.invoice-details td:first-child {
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
</style>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $openreplayUserType = 'internal'; include(__DIR__ . "/includes/openreplay.php"); ?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
79
Layout/default/includes/openreplay.php
Normal file
79
Layout/default/includes/openreplay.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* OpenReplay Session Recording Integration
|
||||
* Include this file in header templates to enable session recording.
|
||||
*
|
||||
* Usage: <?php include(__DIR__ . "/includes/openreplay.php"); ?>
|
||||
*
|
||||
* Variables that can be set before including:
|
||||
* - $openreplayUserType: 'internal' (default) or 'external'
|
||||
* - $openreplayDisabled: set to true to disable tracking
|
||||
*/
|
||||
|
||||
if (!empty($openreplayDisabled)) return;
|
||||
|
||||
$openreplayUserId = '';
|
||||
$openreplayUserName = '';
|
||||
$openreplayUserType = $openreplayUserType ?? 'internal';
|
||||
$openreplayMetadata = [];
|
||||
|
||||
// Get user info for internal users
|
||||
if (class_exists('mfUser') && class_exists('mfLoginController') && mfLoginController::isLoggedIn()) {
|
||||
$user = mfUser::singleton();
|
||||
if ($user && $user->id) {
|
||||
$openreplayUserId = (string) $user->id;
|
||||
$openreplayUserName = $user->username ?? '';
|
||||
$openreplayMetadata['userType'] = $openreplayUserType;
|
||||
$openreplayMetadata['username'] = $openreplayUserName;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow override from JSGlobals (for PWA contexts)
|
||||
if (isset($JSGlobals['OPENREPLAY_USER_ID'])) {
|
||||
$openreplayUserId = (string) $JSGlobals['OPENREPLAY_USER_ID'];
|
||||
}
|
||||
if (isset($JSGlobals['OPENREPLAY_USER_TYPE'])) {
|
||||
$openreplayUserType = $JSGlobals['OPENREPLAY_USER_TYPE'];
|
||||
$openreplayMetadata['userType'] = $openreplayUserType;
|
||||
}
|
||||
if (isset($JSGlobals['OPENREPLAY_COMPANY_ID'])) {
|
||||
$openreplayMetadata['companyId'] = $JSGlobals['OPENREPLAY_COMPANY_ID'];
|
||||
}
|
||||
|
||||
// Disable on dev environment if needed
|
||||
$openreplayEnabled = true;
|
||||
if (defined('MFAPPNAME') && MFAPPNAME === 'devthetool') {
|
||||
// Optionally disable on dev - comment out to enable on dev too
|
||||
// $openreplayEnabled = false;
|
||||
}
|
||||
|
||||
if ($openreplayEnabled):
|
||||
?>
|
||||
<script>
|
||||
var initOpts = {
|
||||
projectKey: "96MdXVcId8Ph3eOirMWj",
|
||||
ingestPoint: "https://openreplay.xinon.at/ingest",
|
||||
defaultInputMode: 2,
|
||||
obscureTextNumbers: false,
|
||||
obscureTextEmails: true,
|
||||
};
|
||||
var startOpts = { userID: <?= json_encode($openreplayUserId) ?> };
|
||||
(function(A,s,a,y,e,r){
|
||||
r=window.OpenReplay=[e,r,y,[s-1, e]];
|
||||
s=document.createElement('script');s.src=A;s.async=!a;
|
||||
document.getElementsByTagName('head')[0].appendChild(s);
|
||||
r.start=function(v){r.push([0])};
|
||||
r.stop=function(v){r.push([1])};
|
||||
r.setUserID=function(id){r.push([2,id])};
|
||||
r.setUserAnonymousID=function(id){r.push([3,id])};
|
||||
r.setMetadata=function(k,v){r.push([4,k,v])};
|
||||
r.event=function(k,p,i){r.push([5,k,p,i])};
|
||||
r.issue=function(k,p){r.push([6,k,p])};
|
||||
r.isActive=function(){return false};
|
||||
r.getSessionToken=function(){};
|
||||
})("//static.openreplay.com/17.0.0/openreplay.js",1,0,initOpts,startOpts);
|
||||
<?php foreach ($openreplayMetadata as $key => $value): ?>
|
||||
window.OpenReplay.setMetadata(<?= json_encode($key) ?>, <?= json_encode($value) ?>);
|
||||
<?php endforeach; ?>
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
@@ -141,9 +141,9 @@
|
||||
<?php if($me->is(["Admin","netowner","pipeplanner","pipeplanner"]) && $me->is("employee")): ?><li><a href="<?=self::getUrl("FiberPlanDispatcher")?>"><i class="fas fa-building-circle-arrow-right text-info"></i> Verteiler und Schächte</a></li><?php endif; ?>
|
||||
<?php if($me->is(["Admin","netowner","lineplanner","lineworker"]) && $me->is("employee")): ?><li><a href="<?=self::getUrl("FiberPlanPipe")?>"><i class="fas fa-pipe text-info pl-1"></i> Rohrverzeichnis</a></li><?php endif; ?>
|
||||
<?php if($me->is(["Admin","netowner","lineplanner","lineworker"]) && $me->is("employee")): ?><li><a href="<?=self::getUrl("FiberPlanCable")?>"><i class="fa-solid fa-timeline text-info "></i> Kabelverzeichnis</a></li><?php endif; ?>
|
||||
<!-- add a new line Arbeitsaufträge for RMLCompany, add a new line Arbeitsaufträge-Management for RMLAdmin -->
|
||||
<?php if($me->can("RMLCompany")): ?><li><a href="<?=self::getUrl("WorkorderCompany")?>"><i class="far fa-fw fa-clipboard-question text-info"></i> Arbeitsaufträge</a></li><?php endif; ?>
|
||||
<?php if($me->can("RMLAdmin")): ?><li><a href="<?=self::getUrl("WorkorderAdmin")?>"><i class="far fa-fw fa-clipboard-question text-info"></i> Arbeitsaufträge-Management</a></li><?php endif; ?>
|
||||
<?php if($me->can("RMLAdmin")): ?><li><a href="<?=self::getUrl("WorkorderDashboard")?>"><i class="far fa-fw fa-chart-line text-info"></i> Arbeitsaufträge-Dashboard</a></li><?php endif; ?>
|
||||
<?php if($me->can("WorkorderMph")): ?><li><a href="<?=self::getUrl("WorkorderMphCompany")?>"><i class="far fa-fw fa-buildings text-info"></i> MPH Arbeitsaufträge</a></li><?php endif; ?>
|
||||
<?php if($me->can("WorkorderMphAdmin")): ?><li><a href="<?=self::getUrl("WorkorderMphAdmin")?>"><i class="far fa-fw fa-buildings text-info"></i> MPH Arbeitsaufträge Verwaltung</a></li><?php endif; ?>
|
||||
</ul>
|
||||
@@ -185,6 +185,7 @@
|
||||
<?php if($me->can("WarehouseAdmin") || $me->can("WarehouseUser")): ?><li><a href="<?=self::getUrl("WarehouseShippingNote")?>"><i class="far fa-fw fa-shipping-fast text-info"></i> Lieferscheine</a></li><?php endif; ?>
|
||||
<?php if($me->can("WarehouseAdmin") || $me->can("WarehouseUser")): ?><li><a href="<?=self::getUrl("WarehouseProject")?>"><i class="fas fa-fw fa-project-diagram text-info"></i> Projekte</a></li><?php endif; ?>
|
||||
<?php if($me->can("WarehouseAdmin")): ?><li><a href="<?=self::getUrl("WarehouseStocktake")?>"><i class="far fa-fw fa-clipboard-check text-info"></i> Inventur</a></li><?php endif; ?>
|
||||
<?php if($me->can("WarehouseAdmin") || $me->can("WarehouseUser")): ?><li><a href="<?=self::getUrl("WarehouseMovement")?>"><i class="far fa-fw fa-arrow-right-arrow-left text-info"></i> Lagerbewegung</a></li><?php endif; ?>
|
||||
|
||||
<?php if($me->can("WarehouseAdmin")): ?><li><a href="<?=self::getUrl("WarehouseDistributor")?>"><i class="far fa-fw fa-cogs text-info"></i> Administration</a></li><?php endif; ?>
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@
|
||||
|
||||
<?php endif; ?>
|
||||
</style>
|
||||
|
||||
<?php $openreplayUserType = 'internal'; include(__DIR__ . "/includes/openreplay.php"); ?>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user