Compare commits

...

43 Commits

Author SHA1 Message Date
a18f74b3df Fixes
Some checks failed
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 35m44s
Gitea Actions / Merge (push) Successful in 7m48s
Gitea Actions / Run-Tests-On-Arm64 (push) Has been cancelled
2026-03-02 14:42:39 +01:00
418e8fcded Fixes 2026-03-02 11:26:36 +01:00
29a88e341f Fixes
Some checks failed
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 21m52s
Gitea Actions / Merge (push) Successful in 7m44s
Gitea Actions / Run-Tests-On-Arm64 (push) Has been cancelled
2026-02-27 15:13:17 +01:00
d7748173a0 Upload 2026-02-26 18:06:56 +01:00
211562f917 Detailseite Buttons 2026-02-25 18:01:39 +01:00
f2336e2996 Fix Graph Calc 2026-02-25 17:33:13 +01:00
755f7e92d4 Backup 2026-02-25 09:51:14 +01:00
4724f0c841 Backup 2026-02-25 09:50:22 +01:00
be27d4b6f0 Fix Invoice TS 2026-02-16 18:10:23 +01:00
9937165710 Fixes
Some checks failed
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 41m47s
Gitea Actions / Merge (push) Successful in 6m45s
Gitea Actions / Run-Tests-On-Arm64 (push) Has been cancelled
2026-02-12 10:31:22 +01:00
a2fc107f26 Backup
Some checks failed
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 39m16s
Gitea Actions / Merge (push) Successful in 6m32s
Gitea Actions / Run-Tests-On-Arm64 (push) Has been cancelled
2026-01-27 09:33:52 +01:00
a2187a48b8 Fixes 2026-01-13 18:22:22 +01:00
d598a9214f Fixes+ 2026-01-13 11:04:34 +01:00
3a0019a1b9 Fixes 2026-01-08 09:48:02 +01:00
7e46c1fc5d Fixes 2026-01-06 10:09:37 +01:00
47057ea7a3 Fixes 2025-12-31 16:47:12 +01:00
e9ca2223e4 Fixes 2025-12-23 14:03:01 +01:00
91ba43510b backup
Some checks failed
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 36m1s
Gitea Actions / Merge (push) Successful in 5m39s
Gitea Actions / Run-Tests-On-Arm64 (push) Has been cancelled
2025-12-19 17:03:27 +01:00
6d6241576d backup 2025-12-17 19:53:54 +01:00
1ff03906c2 backup 2025-12-17 19:32:50 +01:00
7e7628bbd3 Fixes
Some checks failed
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 40m13s
Gitea Actions / Merge (push) Successful in 6m14s
Gitea Actions / Run-Tests-On-Arm64 (push) Has been cancelled
2025-12-16 12:38:53 +01:00
1cc2bc57ba Basket Bundle Attributes 2025-12-15 17:01:07 +01:00
c504f9e4de Basket Bundle Attributes 2025-12-13 18:24:33 +01:00
e044a424fa Fixes 2025-12-13 18:02:25 +01:00
0c00d36236 Fixes 2025-12-12 10:51:44 +01:00
e320761ff5 FIxes 2025-12-11 23:02:19 +01:00
0e1ae9a1a7 FIxes 2025-12-11 22:41:54 +01:00
4db2d79207 FIxes 2025-12-11 21:42:48 +01:00
beb6450338 Fixes 2025-12-11 15:11:46 +01:00
4be0f5a39d first migrate 2025-12-11 09:55:37 +01:00
a65ecc8d47 first migrate 2025-12-11 09:47:54 +01:00
75caff77f1 Backup 2025-12-09 17:17:05 +01:00
ff9978d103 Logs 2025-12-08 15:44:56 +01:00
deca028334 Backup 2025-12-08 15:38:00 +01:00
3fc04a4373 Backup 2025-12-04 14:13:32 +01:00
d30ffb4981 Backup 2025-12-03 12:03:45 +01:00
b6e25299cd Backup 2025-12-03 12:03:04 +01:00
2bc45b89c4 Backup 2025-12-02 13:07:00 +01:00
1021f70a8b Cms 2025-11-04 10:23:20 +01:00
fe28e238be CMS und Status 2025-11-04 09:33:39 +01:00
60a41b5f14 Backup before 2025-11-03 14:33:01 +01:00
3f14580904 Backup 2025-10-24 10:22:54 +02:00
fbd0cbf43e Fix FriendlyCaptcha for Guest Action 2025-10-21 10:47:16 +02:00
887 changed files with 39068 additions and 21860 deletions

View File

@ -41,6 +41,18 @@ services:
- ServerOptions__HostName=smtp4dev - ServerOptions__HostName=smtp4dev
networks: networks:
- network - network
ollama:
image: ollama/ollama:latest
networks:
- network
restart: always
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1 # alternatively, use `count: all` for all GPUs
capabilities: [gpu]
webhook: webhook:
image: tarampampam/webhook-tester:2 image: tarampampam/webhook-tester:2
restart: always restart: always

View File

@ -100,8 +100,14 @@ RUN docker-php-ext-install -j$(nproc) ldap
RUN pecl install imap \ RUN pecl install imap \
&& docker-php-ext-enable imap && docker-php-ext-enable imap
# rsvg und pdfmerge für creativelayouter
COPY ./src/new/assets/fonts /usr/local/share/fonts/custom
RUN fc-cache -fv
RUN apt-get update && apt-get install cairosvg -y
# COPY ./.docker/images/php/base/pdf/php_pdflib.so /pdflib.so RUN apt-get update && apt-get install -y --no-install-recommends \
poppler-utils \
&& rm -rf /var/lib/apt/lists/*
# make bash default shell # make bash default shell
RUN sed -e 's;/bin/ash$;/bin/bash;g' -i /etc/passwd RUN sed -e 's;/bin/ash$;/bin/bash;g' -i /etc/passwd

View File

@ -10,3 +10,7 @@ symfony-init: ## init Symfony
$(EXECUTE_IN_APPLICATION_CONTAINER) php "/application/src/new/bin/console" importmap:install; $(EXECUTE_IN_APPLICATION_CONTAINER) php "/application/src/new/bin/console" importmap:install;
$(EXECUTE_IN_APPLICATION_CONTAINER) php "/application/src/new/bin/console" asset-map:compile; $(EXECUTE_IN_APPLICATION_CONTAINER) php "/application/src/new/bin/console" asset-map:compile;
$(EXECUTE_IN_APPLICATION_CONTAINER) php "/application/src/new/bin/console" assets:install; $(EXECUTE_IN_APPLICATION_CONTAINER) php "/application/src/new/bin/console" assets:install;
.PHONY: php-stan
php-stan: ## init Symfony
$(EXECUTE_IN_APPLICATION_CONTAINER) php "vendor/bin/phpstan" analyse ;

Binary file not shown.

View File

@ -306,7 +306,7 @@ CREATE TABLE `article` (
`a4_auflagen` text DEFAULT NULL, `a4_auflagen` text DEFAULT NULL,
`a4_sponsoring` int(1) DEFAULT NULL, `a4_sponsoring` int(1) DEFAULT NULL,
`a4_prodzeit` varchar(255) DEFAULT NULL, `a4_prodzeit` varchar(255) DEFAULT NULL,
`a4_abpreis` float(8,2) DEFAULT 0.00, `a4_abpreis` varchar(20) DEFAULT NULL,
`a5_buy` int(1) DEFAULT NULL, `a5_buy` int(1) DEFAULT NULL,
`a5_xml` text DEFAULT NULL, `a5_xml` text DEFAULT NULL,
`layouterid` varchar(255) DEFAULT NULL, `layouterid` varchar(255) DEFAULT NULL,
@ -933,7 +933,7 @@ CREATE TABLE `contact_address` (
`email` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL,
`type` int(1) NOT NULL, `type` int(1) NOT NULL,
`company` varchar(100) DEFAULT NULL, `company` varchar(100) DEFAULT NULL,
`anrede` varchar(100) NOT NULL, `anrede` varchar(100) DEFAULT NULL,
`country` varchar(100) DEFAULT NULL, `country` varchar(100) DEFAULT NULL,
`fax` varchar(255) DEFAULT NULL, `fax` varchar(255) DEFAULT NULL,
`company2` varchar(255) DEFAULT NULL, `company2` varchar(255) DEFAULT NULL,
@ -1712,7 +1712,7 @@ CREATE TABLE `papierdb` (
`huelsendurchmesser` varchar(60) DEFAULT NULL, `huelsendurchmesser` varchar(60) DEFAULT NULL,
`kleber` varchar(60) DEFAULT NULL, `kleber` varchar(60) DEFAULT NULL,
`abdeckpapier` varchar(60) DEFAULT NULL, `abdeckpapier` varchar(60) DEFAULT NULL,
`laufrichtung` varchar(2) DEFAULT NULL, `laufrichtung` varchar(60) DEFAULT NULL,
`mengenangabe` int(10) DEFAULT NULL, `mengenangabe` int(10) DEFAULT NULL,
`mengenangabe_palette` int(10) DEFAULT NULL, `mengenangabe_palette` int(10) DEFAULT NULL,
`inhalt` varchar(60) DEFAULT NULL, `inhalt` varchar(60) DEFAULT NULL,
@ -2114,7 +2114,7 @@ CREATE TABLE `shop` (
`smtphostname` varchar(255) DEFAULT NULL, `smtphostname` varchar(255) DEFAULT NULL,
`smtpusername` varchar(255) DEFAULT NULL, `smtpusername` varchar(255) DEFAULT NULL,
`smtppassword` varchar(255) DEFAULT NULL, `smtppassword` varchar(255) DEFAULT NULL,
`smtpusethis` int(1) NOT NULL, `smtpusethis` int(1) NULL DEFAULT 0,
`useemailaslogin` int(1) NOT NULL, `useemailaslogin` int(1) NOT NULL,
`noverify` int(1) NOT NULL, `noverify` int(1) NOT NULL,
`keywords` longtext DEFAULT NULL, `keywords` longtext DEFAULT NULL,

Binary file not shown.

View File

@ -31,4 +31,3 @@ JWT_PASSPHRASE=f7754c7a99638fe7162a144825ddaea7
# postgresql+advisory://db_user:db_password@localhost/db_name # postgresql+advisory://db_user:db_password@localhost/db_name
LOCK_DSN=flock LOCK_DSN=flock
###< symfony/lock ### ###< symfony/lock ###

2
src/new/.env.dev Normal file
View File

@ -0,0 +1,2 @@
MAILER_DSN=smtp://smtp4dev:25

View File

@ -1,27 +1,40 @@
const Order_List_Detail = ({ uuid, basketField1, customerInfo, basketField2, pos, price, product, status, allNet, reOrder, reOrderOrder, reOrderPos }, orderUuid) => ` const Order_List_Detail = ({ uuid, basketField1, customerInfo, basketField2, pos, price, product, status, allNet, reOrder, reOrderOrder, reOrderPos }, orderUuid) => `
<div style="${psc.order.get_pos_bg_color(status)}" class="row ${psc.order.get_pos_bg_color_class(status)}" id="row-${uuid}"> <tr style="${psc.order.get_pos_bg_color(status)}" class="border-t border-stroke ${psc.order.get_pos_bg_color_class(status)}" id="row-${uuid}">
<div class="col-1 p-1">${pos}</div> <td class="px-2 py-3"></td>
<div class="col-4 p-1"> <td class="px-2 py-3 font-medium">${pos}</td>
<td class="px-2 py-3" colspan="2"></td>
<td class="px-2 py-3" colspan="2"></td>
<td class="px-2 py-3"></td>
<td class="px-2 py-3">
${ ${
(product => product.originalProduct ? `<a href="/apps/backend/product/edit/index/${product.originalProduct.uuid}">${product.originalProduct.title}</a>` : `<a href="/apps/backend/product/edit/index/${product.uuid}">${product.title}</a>` )(product) //call the anonymous inline with the data we care about (product => product.originalProduct ? `<a href="/apps/backend/product/edit/index/${product.originalProduct.uuid}" class="text-psc-500 hover:underline">${product.originalProduct.title}</a>` : `<a href="/apps/backend/product/edit/index/${product.uuid}" class="text-psc-500 hover:underline">${product.title}</a>` )(product)
} }
<br/>ArtNr intern: ${product.nrIntern}<br/> <br/><span class="text-xs text-gray-600">ArtNr intern: ${product.nrIntern}</span><br/>
${psc.order.get_special_product_options(product.specialProductTypeObject)}</div> ${psc.order.get_special_product_options(product.specialProductTypeObject)}
<div class="col-1 p-1">Auflage: ${price.count}</div> </td>
<div class="col-1 p-1">Kunden Info: ${customerInfo}${(reOrder? `<br/><strong>Ist eine Nachbestellung</strong>`:``)}</div> <td class="px-2 py-3">
<div class="col-1 p-1">${basketField1}</div> <span class="text-xs">Auflage: ${price.count}</span>
<div class="col-1 p-1">${basketField2}</div> </td>
<div class="col-1 p-1 text-end">${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allNet/100)} <strong>(${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allGross/100)})</strong></div> <td class="px-2 py-3">
<div class="col-2 p-2 text-end"><div class="btn-group btn-group-sm w-100"> <span class="text-xs">Kunden Info: ${customerInfo}${(reOrder? `<br/><strong class="text-yellow-600">Ist eine Nachbestellung</strong>`:``)}</span>
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> </td>
<td class="px-2 py-3 text-right whitespace-nowrap">
${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allNet/100)} <br/><strong>(${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allGross/100)})</strong>
</td>
<td class="px-2 py-3 text-right">
<div class="relative inline-block text-left">
<button type="button" class="inline-flex justify-between items-center w-full px-3 py-1.5 text-xs font-medium text-white bg-psc-500 hover:bg-psc-600 rounded-sm shadow-sm min-w-[120px]">
${psc.order.get_pos_status(status).internalName} ${psc.order.get_pos_status(status).internalName}
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button> </button>
<div class="dropdown-menu"> <div class="hidden absolute right-0 z-10 mt-1 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
${psc.order.get_pos_status_loop(orderUuid, uuid)} ${psc.order.get_pos_status_loop(orderUuid, uuid)}
</div> </div>
</div></div>
<div class="col-1 p-1"></div>
</div> </div>
</td>
</tr>
`; `;
const Order_List_Detail_Simple = ({ uuid, basketField1, customerInfo, basketField2, pos, price, product, status, allNet }, orderUuid) => ` const Order_List_Detail_Simple = ({ uuid, basketField1, customerInfo, basketField2, pos, price, product, status, allNet }, orderUuid) => `
@ -45,7 +58,7 @@ const Order_List_Detail_Simple = ({ uuid, basketField1, customerInfo, basketFiel
`; `;
const Order_List_Pos_Status = (status, orderUuid, posUuid) => ` const Order_List_Pos_Status = (status, orderUuid, posUuid) => `
<button type="button" class="dropdown-item switch_pos" data-order-uuid="${orderUuid}" data-pos-uuid="${posUuid}" data-status="${status.code}">${status.internalName}</button> <button type="button" class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 switch_pos" data-order-uuid="${orderUuid}" data-pos-uuid="${posUuid}" data-status="${status.code}">${status.internalName}</button>
`; `;
const Order_List_Pos_Calc_Option = (elm) => ` const Order_List_Pos_Calc_Option = (elm) => `
@ -92,20 +105,26 @@ export class order {
$('.toogle_list_detail_view').off(); $('.toogle_list_detail_view').off();
$('.toogle_list_detail_view').on('click', function() { $('.toogle_list_detail_view').on('click', function() {
var uuid = $(this).data().uuid; var uuid = $(this).data().uuid;
if($('#rows-' + uuid + ' > div').length > 0) { var $icon = $(this);
if($('#rows-' + uuid).children().length > 0) {
$('#rows-' + uuid).html(''); $('#rows-' + uuid).html('');
$icon.removeClass('fa-minus').addClass('fa-plus');
}else{ }else{
psc.order.load_detail(uuid); psc.order.load_detail(uuid);
$icon.removeClass('fa-plus').addClass('fa-minus');
} }
}); });
$('.toogle_list_simple_detail_view').off(); $('.toogle_list_simple_detail_view').off();
$('.toogle_list_simple_detail_view').on('click', function() { $('.toogle_list_simple_detail_view').on('click', function() {
var uuid = $(this).data().uuid; var uuid = $(this).data().uuid;
if($('#rows-' + uuid + ' > tr').length > 0) { var $icon = $(this);
if($('#rows-' + uuid).children().length > 0) {
$('#rows-' + uuid).html(''); $('#rows-' + uuid).html('');
$icon.removeClass('fa-minus').addClass('fa-plus');
}else{ }else{
psc.order.load_detail(uuid, true); psc.order.load_detail(uuid, true);
$icon.removeClass('fa-plus').addClass('fa-minus');
} }
}); });

View File

@ -2,16 +2,27 @@ import "./css/backend.css"
import Alpine from 'alpinejs'; import Alpine from 'alpinejs';
import persist from '@alpinejs/persist' import persist from '@alpinejs/persist'
import $ from 'jquery' import $ from 'jquery'
window.$ = window.jQuery = $; import 'summernote/dist/summernote-lite.js'
import 'summernote/dist/summernote-lite.min.css'
import '../backend/dashboard/js/summernote/mediabundle.plugin.js'
import { initTabs } from 'david-ai'; import { initTabs } from 'david-ai';
import { registerVueControllerComponents } from '@symfony/ux-vue';
import { startStimulusApp } from '@symfony/stimulus-bundle';
import { order } from './js/order.js';
import { multiselect } from './js/multiselect.js';
window.$ = window.jQuery = $;
// Initialize tabs functionality // Initialize tabs functionality
initTabs(); initTabs();
import { registerVueControllerComponents } from '@symfony/ux-vue';
registerVueControllerComponents() registerVueControllerComponents()
import { startStimulusApp } from '@symfony/stimulus-bundle';
const app = startStimulusApp(); const app = startStimulusApp();
Alpine.plugin(persist) Alpine.plugin(persist)
// Initialize PSC global object
window.psc = window.psc || {};
window.psc.order = new order();
window.psc.multiselect = new multiselect();
Alpine.store('theme', { Alpine.store('theme', {
theme: Alpine.$persist("light").as('theme'), theme: Alpine.$persist("light").as('theme'),
@ -36,3 +47,43 @@ Alpine.store('sideBar', {
} }
}) })
Alpine.start(); Alpine.start();
// Initialize PSC modules
window.psc.order.init();
window.psc.multiselect.init();
const initSummernote = () => {
if (!$.fn?.summernote) {
return;
}
$(document).on('blur', '.note-codable', function () {
const codeviewHtml = $(this).val();
const $summernoteTextarea = $(this).closest('.note-editor').siblings('textarea');
$summernoteTextarea.val(codeviewHtml);
});
const toolbar = [
['style', ['style']],
['font', ['bold', 'italic', 'underline', 'clear']],
['fontsize', ['fontsize']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['height', ['height']],
['table', ['table']],
['insert', ['link', 'hr', 'image']],
['view', ['fullscreen', 'codeview']],
['help', ['help']],
];
if (typeof window.mediaBundleBrowser === 'function') {
toolbar.push(['media', ['media']]);
}
$('.summernote').summernote({
height: 400,
toolbar,
});
};
document.addEventListener('DOMContentLoaded', initSummernote);

View File

@ -57,10 +57,10 @@
} }
.badge-yes { .badge-yes {
@apply inline-flex rounded-sm bg-lime-500 px-2 py-1 text-sm font-medium text-white hover:bg-opacity-90 justify-center shadow-xl; @apply inline-flex items-center rounded-full bg-lime-500 px-2 py-1 text-xs font-medium text-white hover:bg-opacity-90 justify-center shadow-xl;
} }
.badge-no { .badge-no {
@apply inline-flex rounded-sm bg-orange-500 px-2 py-1 text-sm font-medium text-white hover:bg-opacity-90 justify-center shadow-xl; @apply inline-flex items-center rounded-full bg-orange-500 px-2 py-1 text-xs font-medium text-white hover:bg-opacity-90 justify-center shadow-xl;
} }
.table-icon, .button-icon { .table-icon, .button-icon {
@ -68,11 +68,11 @@
} }
.psc-button-save { .psc-button-save {
@apply inline-flex items-center justify-center py-1 gap-1 font-medium rounded-sm px-4 text-sm text-white shadow-lg bg-psc-500 hover:bg-psc-600 hover:ring-2 hover:ring-psc-500 hover:ring-offset-2 min-h-[2.25rem]; @apply inline-flex items-center justify-center py-1 gap-2 font-medium rounded-md px-4 text-sm text-white shadow-lg bg-psc-500 hover:bg-psc-600 hover:ring-2 hover:ring-psc-500 hover:ring-offset-2 min-h-[2.25rem];
} }
.psc-button-secondary { .psc-button-secondary {
@apply inline-flex items-center justify-center py-1 gap-1 font-medium rounded-sm px-4 text-xs text-white shadow-lg bg-psc-500 hover:bg-psc-600 hover:ring-2 hover:ring-psc-500 hover:ring-offset-2 min-h-[1.8rem]; @apply inline-flex items-center justify-center py-1 gap-2 font-medium rounded-md px-4 text-xs text-white shadow-lg bg-psc-500 hover:bg-psc-600 hover:ring-2 hover:ring-psc-500 hover:ring-offset-2 min-h-[1.8rem];
} }
.form-label { .form-label {

View File

@ -0,0 +1,74 @@
export class multiselect {
init() {
this.bindMultiSelectButtons();
}
bindMultiSelectButtons() {
// Find all multiselect widgets
$('[data-multiselect-widget]').each(function() {
const $widget = $(this);
const $from = $widget.find('select[name$="_from"]');
const $to = $widget.find('select[name]:not([name$="_from"])');
// Get base ID (without _from or _to suffix)
const fromId = $from.attr('id');
// Right All button
$widget.find('button[id$="_rightAll"]').off('click').on('click', function() {
$from.find('option').each(function() {
const $option = $(this).clone();
$to.append($option);
});
$from.empty();
});
// Right Selected button
$widget.find('button[id$="_rightSelected"]').off('click').on('click', function() {
$from.find('option:selected').each(function() {
const $option = $(this).clone();
$to.append($option);
$(this).remove();
});
});
// Left Selected button
$widget.find('button[id$="_leftSelected"]').off('click').on('click', function() {
$to.find('option:selected').each(function() {
const $option = $(this).clone();
$from.append($option);
$(this).remove();
});
});
// Left All button
$widget.find('button[id$="_leftAll"]').off('click').on('click', function() {
$to.find('option').each(function() {
const $option = $(this).clone();
$from.append($option);
});
$to.empty();
});
// Double click to move items
$from.off('dblclick').on('dblclick', 'option', function() {
const $option = $(this).clone();
$to.append($option);
$(this).remove();
});
$to.off('dblclick').on('dblclick', 'option', function() {
const $option = $(this).clone();
$from.append($option);
$(this).remove();
});
});
// Before form submit, select all options in the "to" select
$('form').on('submit', function() {
$(this).find('[data-multiselect-widget]').each(function() {
$(this).find('select[name]:not([name$="_from"]) option').prop('selected', true);
});
});
}
}

View File

@ -0,0 +1,315 @@
const Order_List_Detail = ({ uuid, basketField1, customerInfo, basketField2, pos, price, product, status, allNet, reOrder, reOrderOrder, reOrderPos }, orderUuid) => `
<div class="px-6 py-4 bg-gray-50" style="${psc.order.get_pos_bg_color(status)}">
<div class="flex gap-4 items-start" id="row-${uuid}">
<div class="shrink-0 w-28 h-28 rounded-md border-2 border-dashed border-gray-300 bg-white flex flex-col items-center justify-center text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-7 h-7 mb-1">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
<span class="text-xs">Vorschau</span>
</div>
<div class="grid grid-cols-12 gap-4 flex-1">
<div class="col-span-1">
<span class="text-xs font-semibold text-gray-700">Pos:</span>
<div class="font-medium">${pos}</div>
</div>
<div class="col-span-4">
<span class="text-xs font-semibold text-gray-700">Produkt:</span>
<div>
${(product => product.originalProduct ? `<a href="/apps/backend/product/edit/index/${product.originalProduct.uuid}" class="text-psc-500 hover:underline">${product.originalProduct.title}</a>` : `<a href="/apps/backend/product/edit/index/${product.uuid}" class="text-psc-500 hover:underline">${product.title}</a>`)(product)
}
<br/><span class="text-xs text-gray-600">ArtNr intern: ${product.nrIntern}</span><br/>
${psc.order.get_special_product_options(product.specialProductTypeObject)}
</div>
</div>
<div class="col-span-2">
<span class="text-xs font-semibold text-gray-700">Auflage:</span>
<div>${price.count}</div>
</div>
<div class="col-span-2">
<span class="text-xs font-semibold text-gray-700">Kunden Info:</span>
<div class="text-sm">${customerInfo}${(reOrder ? `<br/><strong class="text-yellow-600">Ist eine Nachbestellung</strong>` : ``)}</div>
</div>
<div class="col-span-2">
<span class="text-xs font-semibold text-gray-700">Preis:</span>
<div class="text-right whitespace-nowrap">
${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allNet / 100)}<br/>
<strong>(${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allGross / 100)})</strong>
</div>
</div>
<div class="col-span-1">
<span class="text-xs font-semibold text-gray-700">Status:</span>
<div class="relative inline-block text-left w-full">
<button type="button" class="inline-flex justify-between items-center w-full px-3 py-1.5 text-xs font-medium text-white bg-psc-500 hover:bg-psc-600 rounded-sm shadow-sm">
${psc.order.get_pos_status(status).internalName}
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</button>
<div class="hidden absolute right-0 z-10 mt-1 w-56 rounded-sm shadow-xl bg-white border border-gray-200 py-1">
${psc.order.get_pos_status_loop(orderUuid, uuid)}
</div>
</div>
</div>
</div>
</div>
</div>
`;
const Order_List_Detail_Simple = ({ uuid, basketField1, customerInfo, basketField2, pos, price, product, status, allNet }, orderUuid) => `
<td colspan="12" class="px-6 py-4 bg-gray-50" style="${psc.order.get_pos_bg_color(status)}">
<div class="flex gap-4 w-full">
<div class="flex-none w-16">
<span class="text-xs font-semibold text-gray-700">Pos:</span>
<div class="font-medium">${pos}</div>
</div>
<div class="flex-1">
<span class="text-xs font-semibold text-gray-700">Produkt:</span>
<div>
${(product => product.originalProduct ? `<a href="/apps/backend/product/edit/index/${product.originalProduct.uuid}" class="text-psc-500 hover:underline">${product.originalProduct.title}</a>` : `<a href="/apps/backend/product/edit/index/${product.uuid}" class="text-psc-500 hover:underline">${product.title}</a>`)(product)
}
<br/><span class="text-xs text-gray-600">ArtNr intern: ${product.nrIntern}</span><br/>
${psc.order.get_special_product_options(product.specialProductTypeObject)}
</div>
</div>
<div class="flex-none w-32">
<span class="text-xs font-semibold text-gray-700">Auflage:</span>
<div>${price.count}</div>
</div>
<div class="flex-none w-48">
<span class="text-xs font-semibold text-gray-700">Kunden Info:</span>
<div class="text-sm">${customerInfo}</div>
</div>
<div class="flex-none w-32 text-right">
<span class="text-xs font-semibold text-gray-700">Preis:</span>
<div class="whitespace-nowrap">${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allNet / 100)} <strong>(${new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(price.allGross / 100)})</strong></div>
</div>
<div class="flex-none w-24">
<span class="text-xs font-semibold text-gray-700">Status:</span>
<div>${psc.order.get_pos_status(status).internalName}</div>
</div>
</div>
</td>
`;
const Order_List_Pos_Status = (status, orderUuid, posUuid) => `
<button type="button" class="flex items-center w-full px-3 py-1.5 text-xs text-gray-700 hover:bg-psc-50 hover:text-psc-700 transition-colors switch_pos" data-order-uuid="${orderUuid}" data-pos-uuid="${posUuid}" data-status="${status.code}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-3.5 h-3.5 mr-2 text-gray-400">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
${status.internalName}
</button>
`;
const Order_List_Pos_Calc_Option = (elm) => `
<tr><td>${elm.name}</td><td>${elm.value}</td></tr>
`;
export class order {
init() {
this.load_status();
this.bind_buttons();
this.init_dropdowns();
}
init_dropdowns() {
// Initialize all status dropdowns (both in main table and details)
$(document).off('click.orderDropdown');
$(document).on('click.orderDropdown', '.relative.inline-block button', function (e) {
e.preventDefault();
e.stopPropagation();
var $dropdown = $(this).next('div');
// Close all other dropdowns
$('.relative.inline-block div[class*="hidden"]').addClass('hidden');
// Toggle this dropdown
$dropdown.toggleClass('hidden');
});
// Close dropdowns when clicking outside
$(document).on('click.orderDropdown', function (e) {
if (!$(e.target).closest('.relative.inline-block').length) {
$('.relative.inline-block div[class*="absolute"]').addClass('hidden');
}
});
}
get_special_product_options(specialProductTypeObject) {
if (specialProductTypeObject.typ != 6) {
return '';
}
return '<table>' + specialProductTypeObject.options.map(elm => {
if (elm.type != 'hidden') {
return Order_List_Pos_Calc_Option(elm);
}
}).join('') + '</table>';
}
load_status() {
if (jwt_token != "") {
$.ajax({
url: "/apps/api/system/status",
contentType: "application/json",
headers: {
"Authorization": "Bearer " + jwt_token
},
method: 'GET',
success: function (result) {
psc.order.status_order = result.order;
psc.order.status_pos = result.position;
}
});
}
}
bind_buttons() {
$('.toogle_list_detail_view').off();
$('.toogle_list_detail_view').on('click', function () {
var uuid = $(this).data().uuid;
var $icon = $(this);
if ($('#rows-' + uuid + ' td').children().length > 0) {
$('#rows-' + uuid + ' td').html('');
$icon.removeClass('fa-minus').addClass('fa-plus');
} else {
psc.order.load_detail(uuid);
$icon.removeClass('fa-plus').addClass('fa-minus');
}
});
$('.toogle_list_simple_detail_view').off();
$('.toogle_list_simple_detail_view').on('click', function () {
var uuid = $(this).data().uuid;
var $icon = $(this);
if ($('#rows-' + uuid + ' td').children().length > 0) {
$('#rows-' + uuid + ' td').html('');
$icon.removeClass('fa-minus').addClass('fa-plus');
} else {
psc.order.load_detail(uuid, true);
$icon.removeClass('fa-plus').addClass('fa-minus');
}
});
}
load_detail(orderUuid, simple = false) {
$.ajax({
url: "/apps/api/order/getonebyuuid",
contentType: "application/json",
headers: {
"Authorization": "Bearer " + jwt_token
},
method: 'POST',
data: JSON.stringify({
uuid: orderUuid
}),
success: function (result) {
psc.order.build_details(orderUuid, result, simple);
}
})
}
build_details(orderUuid, result, simple = false) {
if (simple) {
$('#rows-' + result.uuid + ' td').html(result.positions.map(x => Order_List_Detail_Simple(x, orderUuid)).join(''));
} else {
$('#rows-' + result.uuid + ' td').html(result.positions.map(x => Order_List_Detail(x, orderUuid)).join(''));
result.positions.map(x => this.loadPluginDetails(x));
}
this.bind_status_buttons();
}
loadPluginDetails(pos) {
$.ajax({
url: "/apps/api/position/getpluginlistdetails/" + pos.uuid,
contentType: "application/json",
headers: {
"Authorization": "Bearer " + jwt_token
},
method: 'GET',
success: function (result) {
$('#row-' + pos.uuid).append(result.html);
}
})
}
bind_status_buttons() {
// Bind status change buttons for positions
$('.switch_pos').off();
$('.switch_pos').on('click', function () {
var orderUuid = $(this).data().orderUuid;
var posUuid = $(this).data().posUuid;
var status = $(this).data().status;
$.ajax({
url: "/apps/api/position/status/change",
contentType: "application/json",
headers: {
"Authorization": "Bearer " + jwt_token
},
data: JSON.stringify({
position: posUuid,
status: status,
}),
method: 'POST',
success: function (result) {
$('#rows-' + orderUuid + ' td').html('');
psc.order.load_detail(orderUuid);
}
});
});
}
get_pos_bg_color(status) {
/*
{% if orderStatuse.getPosStatusColor(pos.status) != '' %}background-color: {{ orderStatuse.getPosStatusColor(pos.status) }}{% endif %}"
*/
const found = psc.order.status_pos.find(stat => stat.code == status);
if (found && found.color != "") {
return 'background-color: ' + found.color;
}
}
get_pos_status(status) {
/*
{% if orderStatuse.getPosStatusColor(pos.status) != '' %}background-color: {{ orderStatuse.getPosStatusColor(pos.status) }}{% endif %}"
*/
const found = psc.order.status_pos.find(stat => stat.code == status);
if (found) {
return found;
}
return { internalName: 'notFound' };
}
get_pos_bg_color_class(status) {
/*
class="{% if pos.status == 170 %}bg-lightdark{% elseif pos.status == 200 or pos.status == 210 %}bg-lightsuccess{% else %}bg-lightdanger{% endif %}
*/
if (status == 170) {
return 'bg-lightdark';
}
if (status == 200 || status == 210) {
return 'bg-lightsuccess';
}
return 'bg-lightdanger';
}
get_pos_status_loop(orderUuid, posUuid) {
return psc.order.status_pos.map(x => Order_List_Pos_Status(x, orderUuid, posUuid)).join('');
}
}

View File

@ -25,11 +25,10 @@
"cocur/slugify": "v3.1", "cocur/slugify": "v3.1",
"composer/package-versions-deprecated": "^1.8", "composer/package-versions-deprecated": "^1.8",
"ddeboer/imap": "1.21.*", "ddeboer/imap": "1.21.*",
"doctrine/annotations": "^2",
"doctrine/cache": "^2", "doctrine/cache": "^2",
"doctrine/doctrine-bundle": "^2", "doctrine/doctrine-bundle": "^3",
"doctrine/mongodb-odm-bundle": "^5", "doctrine/mongodb-odm-bundle": "^5",
"doctrine/orm": "^2.7", "doctrine/orm": "^3",
"gabrielbull/ups-api": "dev-master", "gabrielbull/ups-api": "dev-master",
"gesdinet/jwt-refresh-token-bundle": "^1.5", "gesdinet/jwt-refresh-token-bundle": "^1.5",
"gregwar/captcha-bundle": "^2.2", "gregwar/captcha-bundle": "^2.2",
@ -37,12 +36,11 @@
"horstoeko/zugferd": "^1.0", "horstoeko/zugferd": "^1.0",
"incenteev/composer-parameter-handler": "^2.0", "incenteev/composer-parameter-handler": "^2.0",
"jms/serializer-bundle": "5.*", "jms/serializer-bundle": "5.*",
"knplabs/knp-gaufrette-bundle": "0.7.*", "knplabs/knp-gaufrette-bundle": "0.9.*",
"knplabs/knp-menu-bundle": "^3", "knplabs/knp-menu-bundle": "^3",
"knplabs/knp-paginator-bundle": "5.9.*", "knplabs/knp-paginator-bundle": "6.10.*",
"lexik/form-filter-bundle": "^7",
"lexik/jwt-authentication-bundle": "^3", "lexik/jwt-authentication-bundle": "^3",
"liip/imagine-bundle": "2.9.*", "liip/imagine-bundle": "2.16.*",
"mistic100/randomcolor": "^1.1", "mistic100/randomcolor": "^1.1",
"mobiledetect/mobiledetectlib": "^2.8", "mobiledetect/mobiledetectlib": "^2.8",
"mpdf/mpdf": "dev-qrcode", "mpdf/mpdf": "dev-qrcode",
@ -50,10 +48,11 @@
"nelmio/api-doc-bundle": "^4", "nelmio/api-doc-bundle": "^4",
"nelmio/cors-bundle": "^2.2", "nelmio/cors-bundle": "^2.2",
"nicolab/php-ftp-client": "^1.4", "nicolab/php-ftp-client": "^1.4",
"oneup/uploader-bundle": "^3", "oneup/uploader-bundle": "^5",
"oyejorge/less.php": "~1.5", "oyejorge/less.php": "~1.5",
"paypal/paypal-checkout-sdk": "dev-master", "paypal/paypal-checkout-sdk": "^1.0",
"paypal/rest-api-sdk-php": "dev-master", "paypal/paypal-server-sdk": "^2",
"phenx/php-font-lib": "^1.0",
"phpoffice/phpspreadsheet": "^1.28", "phpoffice/phpspreadsheet": "^1.28",
"phpseclib/phpseclib": "~3.0", "phpseclib/phpseclib": "~3.0",
"picqer/sendcloud-php-client": "v2.8.1", "picqer/sendcloud-php-client": "v2.8.1",
@ -64,26 +63,29 @@
"ramsey/uuid": "4.5.1", "ramsey/uuid": "4.5.1",
"sauladam/shipment-tracker": "dev-master", "sauladam/shipment-tracker": "dev-master",
"scssphp/scssphp": "v1.11.1", "scssphp/scssphp": "v1.11.1",
"sensio/framework-extra-bundle": "^6.1", "setasign/fpdi-tcpdf": "^2.3",
"sofort/sofortlib-php": "3.3.2", "sofort/sofortlib-php": "3.3.2",
"spatie/array-to-xml": "^3.4", "spatie/array-to-xml": "^3.4",
"spiriitlabs/form-filter-bundle": "12.0.1",
"symfony/ai-agent": "^0.5.0",
"symfony/ai-bundle": "^0.5.0",
"symfony/asset": "*", "symfony/asset": "*",
"symfony/asset-mapper": "6.4.*", "symfony/asset-mapper": "7.4.*",
"symfony/console": "*", "symfony/console": "*",
"symfony/dotenv": "*", "symfony/dotenv": "*",
"symfony/expression-language": "*", "symfony/expression-language": "*",
"symfony/flex": "^1.3.1", "symfony/flex": "^2",
"symfony/form": "*", "symfony/form": "*",
"symfony/framework-bundle": "*", "symfony/framework-bundle": "*",
"symfony/http-client": "*", "symfony/http-client": "*",
"symfony/intl": "*", "symfony/intl": "*",
"symfony/lock": "6.4.*", "symfony/lock": "7.4.*",
"symfony/mailer": "*", "symfony/mailer": "*",
"symfony/mime": "*", "symfony/mime": "*",
"symfony/monolog-bundle": "^3.8", "symfony/monolog-bundle": "^4",
"symfony/notifier": "*", "symfony/notifier": "*",
"symfony/process": "*", "symfony/process": "*",
"symfony/property-access": "6.4.4", "symfony/property-access": "7.4.*",
"symfony/property-info": "*", "symfony/property-info": "*",
"symfony/proxy-manager-bridge": "*", "symfony/proxy-manager-bridge": "*",
"symfony/runtime": "*", "symfony/runtime": "*",
@ -116,9 +118,8 @@
"maglnet/composer-require-checker": "4.*", "maglnet/composer-require-checker": "4.*",
"mockery/mockery": "^1.5", "mockery/mockery": "^1.5",
"php-parallel-lint/php-parallel-lint": "dev-develop", "php-parallel-lint/php-parallel-lint": "dev-develop",
"phpstan/phpstan": "^1", "phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^10", "phpunit/phpunit": "^10",
"rector/rector": "0.19.2",
"squizlabs/php_codesniffer": "*", "squizlabs/php_codesniffer": "*",
"symfony/browser-kit": "*", "symfony/browser-kit": "*",
"symfony/css-selector": "*", "symfony/css-selector": "*",
@ -198,7 +199,7 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "6.4.*" "require": "7.4.*"
}, },
"public-dir": "web/" "public-dir": "web/"
} }

6165
src/new/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,11 @@ return [
Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true], Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true],
Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true],
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true], Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle::class => ['all' => true],
Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true],
Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true], Oneup\UploaderBundle\OneupUploaderBundle::class => ['all' => true],
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
@ -46,6 +44,7 @@ return [
PSC\Shop\CommunicationBundle\PSCShopCommunicationBundle::class => ['all' => true], PSC\Shop\CommunicationBundle\PSCShopCommunicationBundle::class => ['all' => true],
PSC\Component\SteplayouterBundle\PSCComponentSteplayouterBundle::class => ['all' => true], PSC\Component\SteplayouterBundle\PSCComponentSteplayouterBundle::class => ['all' => true],
PSC\Component\ApiBundle\PSCComponentApiBundle::class => ['all' => true], PSC\Component\ApiBundle\PSCComponentApiBundle::class => ['all' => true],
PSC\Component\AiBundle\PSCComponentAiBundle::class => ['all' => true],
PSC\Component\ConfigurationlayouterBundle\PSCComponentConfigurationlayouterBundle::class => ['all' => true], PSC\Component\ConfigurationlayouterBundle\PSCComponentConfigurationlayouterBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true], Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
@ -63,4 +62,6 @@ return [
Symfonycasts\TailwindBundle\SymfonycastsTailwindBundle::class => ['all' => true], Symfonycasts\TailwindBundle\SymfonycastsTailwindBundle::class => ['all' => true],
Symfonycasts\SassBundle\SymfonycastsSassBundle::class => ['all' => true], Symfonycasts\SassBundle\SymfonycastsSassBundle::class => ['all' => true],
Symfony\UX\Vue\VueBundle::class => ['all' => true], Symfony\UX\Vue\VueBundle::class => ['all' => true],
Spiriit\Bundle\FormFilterBundle\SpiriitFormFilterBundle::class => ['all' => true],
Symfony\AI\AiBundle\AiBundle::class => ['all' => true],
]; ];

View File

@ -0,0 +1,27 @@
ai:
platform:
# Inference Platform configuration
# see https://github.com/symfony/ai/tree/main/src/platform#platform-bridges
# openai:
# api_key: '%env(OPENAI_API_KEY)%'
agent:
# Agent configuration
# see https://symfony.com/doc/current/ai/bundles/ai-bundle.html
# default:
# platform: 'ai.platform.openai'
# model: 'gpt-5-mini'
# prompt: |
# You are a pirate and you write funny.
# tools:
# - 'Symfony\AI\Agent\Bridge\Clock\Clock'
store:
# Store configuration
# chromadb:
# default:
# client: 'client.service.id'
# collection: 'my_collection'

View File

@ -7,14 +7,22 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigura
return static function (ContainerConfigurator $containerConfigurator): void { return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension( $containerConfigurator->extension(
'monolog', [ 'monolog', [
'channels' => ['ai'],
'handlers' => [ 'handlers' => [
'ai' => [
'type' => 'stream',
'path' => '%kernel.logs_dir%/ai.log',
'level' => 'debug',
'channels' => ['ai'],
],
'main' => [ 'main' => [
'type' => 'stream', 'type' => 'stream',
'path' => '%kernel.logs_dir%/%kernel.environment%.log', 'path' => '%kernel.logs_dir%/%kernel.environment%.log',
'level' => 'debug', 'level' => 'debug',
'channels' => [ 'channels' => [
'!event', '!event',
'!php' '!php',
'!ai',
] ]
], ],
'console' => [ 'console' => [
@ -23,7 +31,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'channels' => [ 'channels' => [
'!event', '!event',
'!doctrine', '!doctrine',
'!console' '!console',
'!ai',
] ]
] ]
] ]

View File

@ -6,23 +6,21 @@ use PSC\Libraries\DoctrineBundle\ORM\Query\AST\Functions\SimpleFunction;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void { return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension( $containerConfigurator->extension('doctrine', [
'doctrine',
[
'dbal' => [ 'dbal' => [
'url' => '%env(resolve:DATABASE_URL)%', 'url' => '%env(resolve:DATABASE_URL)%',
'charset' => 'utf8mb4', 'charset' => 'utf8mb4',
'default_table_options' => ['collate' => 'utf8mb4_unicode_ci']], 'default_table_options' => ['collate' => 'utf8mb4_unicode_ci'],
'server_version' => '8.0',
],
'orm' => [ 'orm' => [
'auto_generate_proxy_classes' => true,
'naming_strategy' => 'doctrine.orm.naming_strategy.underscore_number_aware', 'naming_strategy' => 'doctrine.orm.naming_strategy.underscore_number_aware',
'auto_mapping' => true, 'auto_mapping' => true,
'dql' => [ 'dql' => [
'numeric_functions' => [ 'numeric_functions' => [
'month' => SimpleFunction::class 'month' => SimpleFunction::class,
] ],
] ],
] ],
] ]);
);
}; };

View File

@ -5,7 +5,14 @@ declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void { return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('doctrine', ['orm' => ['auto_generate_proxy_classes' => false, 'metadata_cache_driver' => ['type' => 'pool', 'pool' => 'doctrine.system_cache_pool'], 'query_cache_driver' => ['type' => 'pool', 'pool' => 'doctrine.system_cache_pool'], 'result_cache_driver' => ['type' => 'pool', 'pool' => 'doctrine.result_cache_pool']]]); $containerConfigurator->extension('doctrine', ['orm' => [
'metadata_cache_driver' => ['type' => 'pool', 'pool' => 'doctrine.system_cache_pool'],
'query_cache_driver' => ['type' => 'pool', 'pool' => 'doctrine.system_cache_pool'],
'result_cache_driver' => ['type' => 'pool', 'pool' => 'doctrine.result_cache_pool'],
]]);
$containerConfigurator->extension('framework', ['cache' => ['pools' => ['doctrine.result_cache_pool' => ['adapter' => 'cache.app'], 'doctrine.system_cache_pool' => ['adapter' => 'cache.system']]]]); $containerConfigurator->extension('framework', ['cache' => ['pools' => [
'doctrine.result_cache_pool' => ['adapter' => 'cache.app'],
'doctrine.system_cache_pool' => ['adapter' => 'cache.system'],
]]]);
}; };

View File

@ -5,11 +5,56 @@ declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void { return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('psc_shop_media', ['default_context' => 'default', 'contexts' => ['default' => ['providers' => ['psc.shop.media.provider.image', 'psc.shop.media.provider.file'], 'formats' => ['small' => ['width' => 100, 'quality' => 70], 'big' => ['width' => 500, 'quality' => 70]]]], 'providers' => ['image' => ['thumbnail' => 'psc.shop.media.thumbnail.liip_imagine', 'allowed_extensions' => ['jpg', 'png', 'gif', 'jpeg'], 'allowed_mime_types' => ['image/pjpeg', 'image/jpeg', 'image/png', 'image/x-png', 'image/gif']]], 'cdn' => ['server' => ['path' => '/uploads/media']], 'filesystem' => ['local' => ['directory' => '%kernel.project_dir%/web/uploads/media', 'create' => false]]]); $containerConfigurator->extension('psc_shop_media', [
'default_context' => 'default',
'contexts' => ['default' => [
'providers' => ['psc.shop.media.provider.image', 'psc.shop.media.provider.file'],
'formats' => ['small' => ['width' => 100, 'quality' => 70], 'big' => ['width' => 500, 'quality' => 70]],
]],
'providers' => ['image' => [
'thumbnail' => 'psc.shop.media.thumbnail.liip_imagine',
'allowed_extensions' => ['jpg', 'png', 'gif', 'jpeg'],
'allowed_mime_types' => ['image/pjpeg', 'image/jpeg', 'image/png', 'image/x-png', 'image/gif'],
]],
'cdn' => ['server' => ['path' => '/uploads/media']],
'filesystem' => ['local' => ['directory' => '%kernel.project_dir%/web/uploads/media', 'create' => false]],
]);
$containerConfigurator->extension('knp_gaufrette', ['stream_wrapper' => null, 'adapters' => ['backend_articles' => ['safe_local' => ['directory' => '%kernel.project_dir%/web/uploads/backend_articles', 'create' => true]], 'steplayouter_motiv_guest' => ['local' => ['directory' => '%kernel.project_dir%/web/uploads/steplayouter_motiv_guest', 'create' => true]], 'steplayouter_motiv' => ['local' => ['directory' => '%kernel.project_dir%/web/market/motive/', 'create' => true]]], 'filesystems' => ['backend_articles' => ['adapter' => 'backend_articles', 'alias' => 'backend_articles_filesystem'], 'steplayouter_motiv_guest' => ['adapter' => 'steplayouter_motiv_guest'], 'steplayouter_motiv' => ['adapter' => 'steplayouter_motiv']]]); $containerConfigurator->extension('knp_gaufrette', [
'stream_wrapper' => null,
'adapters' => [
'backend_articles' => ['safe_local' => [
'directory' => '%kernel.project_dir%/web/uploads/backend_articles',
'create' => true,
]],
'steplayouter_motiv_guest' => ['local' => [
'directory' => '%kernel.project_dir%/web/uploads/steplayouter_motiv_guest',
'create' => true,
]],
'steplayouter_motiv' => ['local' => [
'directory' => '%kernel.project_dir%/web/market/motive/',
'create' => true,
]],
],
'filesystems' => [
'backend_articles' => ['adapter' => 'backend_articles', 'alias' => 'backend_articles_filesystem'],
'steplayouter_motiv_guest' => ['adapter' => 'steplayouter_motiv_guest'],
'steplayouter_motiv' => ['adapter' => 'steplayouter_motiv'],
],
]);
$containerConfigurator->extension('knp_paginator', ['page_range' => 5, 'default_options' => ['page_name' => 'page', 'sort_field_name' => 'sort', 'sort_direction_name' => 'direction', 'distinct' => true], 'template' => ['pagination' => '@PSCBackendDashboard/pagination/bootstrap4.html.twig']]); $containerConfigurator->extension('knp_paginator', [
'page_range' => 5,
'default_options' => [
'page_name' => 'page',
'sort_field_name' => 'sort',
'sort_direction_name' => 'direction',
'distinct' => true,
],
'template' => ['pagination' => '@PSCBackendDashboard/pagination/bootstrap4.html.twig'],
]);
$containerConfigurator->extension('lexik_form_filter', ['listeners' => ['doctrine_orm' => true, 'doctrine_dbal' => true, 'doctrine_mongodb' => true]]); $containerConfigurator->extension('spiriit_form_filter', ['listeners' => [
'doctrine_orm' => true,
]]);
}; };

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('sensio_framework_extra', ['router' => ['annotations' => false], 'request' => ['converters' => true, 'disable' => ['doctrine.orm', 'datetime']]]);
};

View File

@ -5,5 +5,21 @@ declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void { return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('monolog', ['handlers' => ['main' => ['type' => 'stream', 'path' => '%kernel.logs_dir%/%kernel.environment%.log', 'level' => 'debug', 'channels' => ['!event']]]]); $containerConfigurator->extension('monolog', [
'channels' => ['ai'],
'handlers' => [
'ai' => [
'type' => 'stream',
'path' => '%kernel.logs_dir%/ai.log',
'level' => 'debug',
'channels' => ['ai'],
],
'main' => [
'type' => 'stream',
'path' => '%kernel.logs_dir%/%kernel.environment%.log',
'level' => 'debug',
'channels' => ['!event'],
],
],
]);
}; };

View File

@ -7,6 +7,7 @@ use PSC\Shop\OrderBundle\Service\Order;
use PSC\System\SettingsBundle\Service\Instance; use PSC\System\SettingsBundle\Service\Instance;
use PSC\System\SettingsBundle\Service\Shop; use PSC\System\SettingsBundle\Service\Shop;
use PSC\System\SettingsBundle\Service\Token; use PSC\System\SettingsBundle\Service\Token;
use PSC\System\SettingsBundle\Service\Version;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service; use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
@ -21,7 +22,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'shopService' => service(Shop::class), 'shopService' => service(Shop::class),
'orderService' => service(Order::class), 'orderService' => service(Order::class),
'tokenService' => service(Token::class), 'tokenService' => service(Token::class),
'mediaService' => service(MediaManager::class) 'mediaService' => service(MediaManager::class),
'versionService' => service(Version::class)
] ]
] ]
); );

3171
src/new/config/reference.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@ return static function (RoutingConfigurator $routingConfigurator): void {
$routingConfigurator->import('@PSCComponentApiBundle/Resources/config/routing.yml'); $routingConfigurator->import('@PSCComponentApiBundle/Resources/config/routing.yml');
$routingConfigurator->import('@PSCComponentAiBundle/Resources/config/routing.yml');
$routingConfigurator->import('@PSCBackendDashboardBundle/Resources/config/routing.yml'); $routingConfigurator->import('@PSCBackendDashboardBundle/Resources/config/routing.yml');
$routingConfigurator->import('@PSCBackendDomainBundle/Resources/config/routing.yml'); $routingConfigurator->import('@PSCBackendDomainBundle/Resources/config/routing.yml');
@ -53,14 +55,14 @@ return static function (RoutingConfigurator $routingConfigurator): void {
$routingConfigurator->import('@PSCShopCreateBundle/Resources/config/routing.yml'); $routingConfigurator->import('@PSCShopCreateBundle/Resources/config/routing.yml');
$routingConfigurator->import('@PSCComponentSteplayouterBundle/Controller/', 'annotation')->prefix('/'); $routingConfigurator->import('@PSCComponentSteplayouterBundle/Controller/', 'attribute')->prefix('/');
$routingConfigurator $routingConfigurator
->import('@PSCComponentSteplayouterBundle/Controller/System', 'annotation') ->import('@PSCComponentSteplayouterBundle/Controller/System', 'attribute')
->prefix('/step/system'); ->prefix('/step/system');
$routingConfigurator $routingConfigurator
->import('@PSCComponentConfigurationlayouterBundle/Controller/', 'annotation') ->import('@PSCComponentConfigurationlayouterBundle/Controller/', 'attribute')
->prefix('/psc/component'); ->prefix('/psc/component');
$routingConfigurator->import('.', 'plugin'); $routingConfigurator->import('.', 'plugin');

View File

@ -5,5 +5,5 @@ declare(strict_types=1);
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
return static function (RoutingConfigurator $routingConfigurator): void { return static function (RoutingConfigurator $routingConfigurator): void {
$routingConfigurator->import('../../src/PSC/Kernel.php', 'annotation'); $routingConfigurator->import('../../src/PSC/Kernel.php', 'attribute');
}; };

View File

@ -20,11 +20,11 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services(); $services = $containerConfigurator->services();
$services->defaults() $services->defaults()->autowire()->autoconfigure();
->autowire()
->autoconfigure();
$services->set('liip_imagine.data.loader.stream.steplayouter_motiv_guest', StreamLoader::class) /* $services
->args([service('liip_imagine'), 'gaufrette://steplayouter_motiv_guest/']) * ->set('liip_imagine.data.loader.stream.steplayouter_motiv_guest', StreamLoader::class)
->tag('liip_imagine.data.loader', ['loader' => 'stream.steplayouter_motiv_guest']); * ->args([service('liip_imagine'), 'gaufrette://steplayouter_motiv_guest/'])->tag('liip_imagine.data.loader', [
* 'loader' => 'stream.steplayouter_motiv_guest',
* ]);*/
}; };

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ PSC\Shop\EntityBundle\Entity\Product:
pos: <numberBetween(1, 200)> pos: <numberBetween(1, 200)>
notEdit: false notEdit: false
private: false private: false
taxClass: 19 mwert: 19
price: 0 price: 0
set_config: '{}' set_config: '{}'
shop: '@shop_1' shop: '@shop_1'
@ -23,7 +23,7 @@ PSC\Shop\EntityBundle\Entity\Product:
notEdit: false notEdit: false
pos: <numberBetween(1, 200)> pos: <numberBetween(1, 200)>
private: false private: false
taxClass: 19 mwert: 19
price: 0 price: 0
set_config: '{}' set_config: '{}'
shop: '@shop_1' shop: '@shop_1'
@ -37,7 +37,7 @@ PSC\Shop\EntityBundle\Entity\Product:
notEdit: false notEdit: false
pos: <numberBetween(1, 200)> pos: <numberBetween(1, 200)>
private: false private: false
taxClass: 19 mwert: 19
price: 0 price: 0
set_config: '\[{"article_id": "7996f00d-a5e6-4915-970d-9f7c4287cd92"}, {"article_id": "7996f00d-a5e6-4915-970d-9f7c4287cd91"} ]' set_config: '\[{"article_id": "7996f00d-a5e6-4915-970d-9f7c4287cd92"}, {"article_id": "7996f00d-a5e6-4915-970d-9f7c4287cd91"} ]'
shop: '@shop_1' shop: '@shop_1'
@ -51,7 +51,7 @@ PSC\Shop\EntityBundle\Entity\Product:
type: 2 type: 2
pos: <numberBetween(1, 200)> pos: <numberBetween(1, 200)>
private: false private: false
taxClass: 19 mwert: 19
price: 20 price: 20
set_config: '{}' set_config: '{}'
shop: '@shop_1' shop: '@shop_1'
@ -65,7 +65,7 @@ PSC\Shop\EntityBundle\Entity\Product:
pos: <numberBetween(1, 200)> pos: <numberBetween(1, 200)>
notEdit: false notEdit: false
private: false private: false
taxClass: 19 mwert: 19
set_config: '{}' set_config: '{}'
shop: '@shop_1' shop: '@shop_1'
calcXml: > calcXml: >
@ -116,7 +116,7 @@ PSC\Shop\EntityBundle\Entity\Product:
pos: <numberBetween(1, 200)> pos: <numberBetween(1, 200)>
notEdit: false notEdit: false
private: false private: false
taxClass: 19 mwert: 19
set_config: '{}' set_config: '{}'
shop: '@shop_1' shop: '@shop_1'
calcXml: > calcXml: >

View File

@ -9,7 +9,7 @@ PSC\Shop\EntityBundle\Entity\Voucher:
code: 5f code: 5f
mode: 1 mode: 1
fromDate: <(new DateTime("2023-12-12"))> fromDate: <(new DateTime("2023-12-12"))>
toDate: <(new DateTime("2025-12-12"))> toDate: <(new DateTime("2026-12-12"))>
value: 5 value: 5
shop: '@shop_1' shop: '@shop_1'
voucher_2: voucher_2:
@ -22,7 +22,7 @@ PSC\Shop\EntityBundle\Entity\Voucher:
code: 5p code: 5p
mode: 1 mode: 1
fromDate: <(new DateTime("2023-12-12"))> fromDate: <(new DateTime("2023-12-12"))>
toDate: <(new DateTime("2025-12-12"))> toDate: <(new DateTime("2026-12-12"))>
value: 5 value: 5
shop: '@shop_1' shop: '@shop_1'
voucher_3: voucher_3:
@ -37,7 +37,7 @@ PSC\Shop\EntityBundle\Entity\Voucher:
zeroShipping: false zeroShipping: false
mode: 1 mode: 1
fromDate: <(new DateTime("2023-12-12"))> fromDate: <(new DateTime("2023-12-12"))>
toDate: <(new DateTime("2025-12-12"))> toDate: <(new DateTime("2026-12-12"))>
value: 0 value: 0
shop: '@shop_1' shop: '@shop_1'
voucher_4: voucher_4:
@ -52,7 +52,7 @@ PSC\Shop\EntityBundle\Entity\Voucher:
zeroShipping: true zeroShipping: true
mode: 1 mode: 1
fromDate: <(new DateTime("2023-12-12"))> fromDate: <(new DateTime("2023-12-12"))>
toDate: <(new DateTime("2025-12-12"))> toDate: <(new DateTime("2026-12-12"))>
value: 0 value: 0
shop: '@shop_1' shop: '@shop_1'

5
src/new/phpstan.neon Normal file
View File

@ -0,0 +1,5 @@
parameters:
level: 7
paths:
- src

View File

@ -1780,7 +1780,7 @@ CREATE TABLE `shop` (
`smtphostname` varchar(255) NOT NULL, `smtphostname` varchar(255) NOT NULL,
`smtpusername` varchar(255) NOT NULL, `smtpusername` varchar(255) NOT NULL,
`smtppassword` varchar(255) NOT NULL, `smtppassword` varchar(255) NOT NULL,
`smtpusethis` int(1) NOT NULL, `smtpusethis` int(1) NULL DEFAULT 0,
`useemailaslogin` int(1) NOT NULL, `useemailaslogin` int(1) NOT NULL,
`noverify` int(1) NOT NULL, `noverify` int(1) NOT NULL,
`keywords` longtext, `keywords` longtext,

View File

@ -20,11 +20,11 @@ use PSC\Shop\QueueBundle\Service\Queue\Manager;
use PSC\System\SettingsBundle\Service\Instance; use PSC\System\SettingsBundle\Service\Instance;
use PSC\System\SettingsBundle\Service\Shop; use PSC\System\SettingsBundle\Service\Shop;
use PSC\System\UpdateBundle\Service\Migration; use PSC\System\UpdateBundle\Service\Migration;
use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Intl\NumberFormatter\NumberFormatter; use Symfony\Component\Intl\NumberFormatter\NumberFormatter;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\UX\Chartjs\Builder\ChartBuilderInterface; use Symfony\UX\Chartjs\Builder\ChartBuilderInterface;
use Symfony\UX\Chartjs\Model\Chart; use Symfony\UX\Chartjs\Model\Chart;
@ -47,24 +47,52 @@ class DashboardController extends AbstractController
* @return array|View|\Symfony\Component\HttpFoundation\RedirectResponse * @return array|View|\Symfony\Component\HttpFoundation\RedirectResponse
* @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\ORMException
*/ */
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
#[Route(path: '/dashboard', name: 'psc_backend_dashboard_index')] #[Route(path: '/dashboard', name: 'psc_backend_dashboard_index')]
#[Template] #[Template('@PSCBackendDashboard/dashboard/index.html.twig')]
public function indexAction(Migration $migration, Shop $shop, EntityManagerInterface $entityManager, Manager $queueService, Instance $instanceService, ChartBuilderInterface $chartBuilder, ContactRepository $contactRepository, Order $orderService) public function indexAction(
{ Migration $migration,
Shop $shop,
EntityManagerInterface $entityManager,
Manager $queueService,
Instance $instanceService,
ChartBuilderInterface $chartBuilder,
ContactRepository $contactRepository,
Order $orderService,
) {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */
$selectedShop = $shop->getSelectedShop(); $selectedShop = $shop->getSelectedShop();
$year1 = date("Y"); $year1 = date('Y');
$year2 = date("Y") - 1; $year2 = date('Y') - 1;
$year3 = date("Y") - 2; $year3 = date('Y') - 2;
$tempSales1 = array_column($entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order')->getIncomeByShop($selectedShop->getUid(), $year1), 'brutto', 'monat'); $tempSales1 = array_column(
$tempSales2 = array_column($entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order')->getIncomeByShop($selectedShop->getUid(), $year2), 'brutto', 'monat'); $entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order')->getIncomeByShop(
$tempSales3 = array_column($entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order')->getIncomeByShop($selectedShop->getUid(), $year3), 'brutto', 'monat'); $selectedShop->getUid(),
$year1,
),
'brutto',
'monat',
);
$tempSales2 = array_column(
$entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order')->getIncomeByShop(
$selectedShop->getUid(),
$year2,
),
'brutto',
'monat',
);
$tempSales3 = array_column(
$entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order')->getIncomeByShop(
$selectedShop->getUid(),
$year3,
),
'brutto',
'monat',
);
$fmt = new \NumberFormatter(locale_get_default(), \NumberFormatter::CURRENCY); $fmt = new \NumberFormatter(locale_get_default(), \NumberFormatter::CURRENCY);
$sales1 = array(); $sales1 = [];
$sales2 = array(); $sales2 = [];
$sales3 = array(); $sales3 = [];
for ($i = 1; $i <= 12; $i++) { for ($i = 1; $i <= 12; $i++) {
if (isset($tempSales1[$i])) { if (isset($tempSales1[$i])) {
$sales1[] = round($tempSales1[$i]); $sales1[] = round($tempSales1[$i]);
@ -84,7 +112,20 @@ class DashboardController extends AbstractController
} }
$chart = $chartBuilder->createChart(Chart::TYPE_LINE); $chart = $chartBuilder->createChart(Chart::TYPE_LINE);
$chart->setData([ $chart->setData([
'labels' => ['Januar', 'Februar', 'März', 'April', 'May', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], 'labels' => [
'Januar',
'Februar',
'März',
'April',
'May',
'Juni',
'Juli',
'August',
'September',
'Oktober',
'November',
'Dezember',
],
'datasets' => [ 'datasets' => [
[ [
'label' => $year1, 'label' => $year1,
@ -113,19 +154,18 @@ class DashboardController extends AbstractController
'maintainAspectRatio' => false, 'maintainAspectRatio' => false,
]); ]);
if ($migration->checkIfMigrationMustRun()) { if ($migration->checkIfMigrationMustRun()) {
return $this->redirectToRoute('psc_system_update_migration_do'); return $this->redirectToRoute('psc_system_update_migration_do');
} }
return array( return [
'user' => $this->getUser(), 'user' => $this->getUser(),
'shop' => $selectedShop, 'shop' => $selectedShop,
'lastContacts' => $contactRepository->getLastCreatedContacts($selectedShop, 10), 'lastContacts' => $contactRepository->getLastCreatedContacts($selectedShop, 10),
'lastOrders' => $orderService->getLastOrders(), 'lastOrders' => $orderService->getLastOrders(),
'queueErrorCount' => $queueService->getErrorJobCount(), 'queueErrorCount' => $queueService->getErrorJobCount(),
'instance' => $instanceService->getInstance(), 'instance' => $instanceService->getInstance(),
'chart' => $chart 'chart' => $chart,
); ];
} }
} }

View File

@ -15,9 +15,9 @@ namespace PSC\Backend\DashboardBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
/** /**
* DefaultController fürs ProductionBundle * DefaultController fürs ProductionBundle
@ -34,7 +34,7 @@ class DefaultController extends AbstractController
* @return View * @return View
*/ */
#[Route(path: '/', name: 'psc_backend_default_index')] #[Route(path: '/', name: 'psc_backend_default_index')]
#[Template] #[Template('@PSCBackendDashboard/default/index.html.twig')]
public function indexAction() public function indexAction()
{ {
return $this->redirect($this->generateUrl("psc_backend_dashboard_index")); return $this->redirect($this->generateUrl("psc_backend_dashboard_index"));

View File

@ -16,16 +16,16 @@ namespace PSC\Backend\DashboardBundle\Controller;
use Http\Message\Authentication; use Http\Message\Authentication;
use PSC\System\UpdateBundle\Service\Migration; use PSC\System\UpdateBundle\Service\Migration;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class LoginController extends AbstractController class LoginController extends AbstractController
{ {
#[Route(path: '/login', name: 'psc_backend_login')] #[Route(path: '/login', name: 'psc_backend_login')]
#[Template] #[Template('@PSCBackendDashboard/login/index.html.twig')]
public function indexAction(Migration $migration, AuthenticationUtils $authenticationUtils) public function indexAction(Migration $migration, AuthenticationUtils $authenticationUtils)
{ {
if ($migration->checkIfMigrationMustRun()) { if ($migration->checkIfMigrationMustRun()) {

View File

@ -17,103 +17,97 @@ use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use PSC\Shop\EntityBundle\Entity\Domain; use PSC\Shop\EntityBundle\Entity\Domain;
use PSC\System\SettingsBundle\Service\Shop; use PSC\System\SettingsBundle\Service\Shop;
use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* ShopController fürs ProductionBundle
*
* @package PSC\Backend\Production
* @subpackage Controller
*/
class ShopController extends AbstractController class ShopController extends AbstractController
{ {
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
#[Template] #[Template('@PSCBackendDashboard/shop/my_editable_shops.html.twig')]
public function myEditableShopsAction(Request $request, Shop $shop, JWTTokenManagerInterface $jwtManager, EntityManagerInterface $entityManager) public function myEditableShopsAction(
{ Request $request,
Shop $shop,
JWTTokenManagerInterface $jwtManager,
EntityManagerInterface $entityManager,
) {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */
$selectedShop = $shop->getSelectedShop(); $selectedShop = $shop->getSelectedShop();
$shops = $shop->getMyEditableShops(); $shops = $shop->getMyEditableShops();
$domains = $entityManager $domains = $entityManager->getRepository(Domain::class)->getAllByShopId($selectedShop);
->getRepository(Domain::class)->getAllByShopId($selectedShop);
if (count($domains) > 0) { if (count($domains) > 0) {
$domain = array_pop($domains)->getHost(); $domain = array_pop($domains)->getHost();
} else { } else {
$domain = false; $domain = false;
} }
return array('domain' => $domain, 'shops' => $shops, 'jwt' => $jwtManager->create($this->getUser()), 'selectedShop' => $selectedShop, 'displayDeletedShop' => $request->getSession()->get('displayDeletedShop', false)); return [
'domain' => $domain,
'shops' => $shops,
'jwt' => $jwtManager->create($this->getUser()),
'selectedShop' => $selectedShop,
'displayDeletedShop' => $request->getSession()->get('displayDeletedShop', false),
];
} }
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
#[Template] #[Template('@PSCBackendDashboard/shop/my_editable_shops_tailwind.html.twig')]
public function myEditableShopsTailwindAction(Request $request, Shop $shop, JWTTokenManagerInterface $jwtManager, EntityManagerInterface $entityManager) public function myEditableShopsTailwindAction(
{ Request $request,
Shop $shop,
JWTTokenManagerInterface $jwtManager,
EntityManagerInterface $entityManager,
) {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */
$selectedShop = $shop->getSelectedShop(); $selectedShop = $shop->getSelectedShop();
$shops = $shop->getMyEditableShops(); $shops = $shop->getMyEditableShops();
$domains = $entityManager $domains = $entityManager->getRepository(Domain::class)->getAllByShopId($selectedShop);
->getRepository(Domain::class)->getAllByShopId($selectedShop);
if (count($domains) > 0) { if (count($domains) > 0) {
$domain = array_pop($domains)->getHost(); $domain = array_pop($domains)->getHost();
} else { } else {
$domain = false; $domain = false;
} }
return array('domain' => $domain, 'shops' => $shops, 'jwt' => $jwtManager->create($this->getUser()), 'selectedShop' => $selectedShop, 'displayDeletedShop' => $request->getSession()->get('displayDeletedShop', false)); return [
'domain' => $domain,
'shops' => $shops,
'jwt' => $jwtManager->create($this->getUser()),
'selectedShop' => $selectedShop,
'displayDeletedShop' => $request->getSession()->get('displayDeletedShop', false),
];
} }
#[IsGranted('ROLE_SHOP')]
/**
* Change Shop
*
*
* @param Request $request
* @param EntityManagerInterface $em
* @param \Symfony\Component\Security\Core\Security $security
* @param $shop_uuid
* @return RedirectResponse
*/
#[Security("is_granted('ROLE_SHOP')")]
#[Route(path: '/shop/change/{shop_uuid}', name: 'psc_backend_dashboard_shop_change')] #[Route(path: '/shop/change/{shop_uuid}', name: 'psc_backend_dashboard_shop_change')]
public function changeShopAction(Request $request, EntityManagerInterface $em, \Symfony\Component\Security\Core\Security $security, $shop_uuid) public function changeShopAction(Request $request, EntityManagerInterface $em, Security $security, $shop_uuid)
{ {
$em $em->getRepository('PSC\Shop\EntityBundle\Entity\ShopContact')->changeSelectedShop(
->getRepository('PSC\Shop\EntityBundle\Entity\ShopContact') $security->getUser(),
->changeSelectedShop($security->getUser(), $shop_uuid); $shop_uuid,
);
$request->getSession()->set('selectedShop', $shop_uuid); $request->getSession()->set('selectedShop', $shop_uuid);
return $this->redirect($this->generateUrl('psc_backend_dashboard_index')); return $this->redirect($this->generateUrl('psc_backend_dashboard_index'));
} }
/** #[IsGranted('ROLE_SHOP')]
* Change Shop
*
*
* @param Request $request
* @param EntityManagerInterface $em
* @param \Symfony\Component\Security\Core\Security $security
* @param Shop $shopService
* @return RedirectResponse
* @throws \Doctrine\ORM\ORMException
*/
#[Security("is_granted('ROLE_SHOP')")]
#[Route(path: '/shop/deleted/toogle', name: 'psc_backend_dashboard_toogle_deleted_shop')] #[Route(path: '/shop/deleted/toogle', name: 'psc_backend_dashboard_toogle_deleted_shop')]
public function toogleDisplayDeletedShopAction(Request $request, EntityManagerInterface $em, \Symfony\Component\Security\Core\Security $security, Shop $shopService) public function toogleDisplayDeletedShopAction(
{ Request $request,
EntityManagerInterface $em,
Security $security,
Shop $shopService,
) {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */
$selectedShop = $shopService->getSelectedShop(); $selectedShop = $shopService->getSelectedShop();
if (!!$request->getSession()->get('displayDeletedShop', false) && $selectedShop->isDeleted()) { if (!!$request->getSession()->get('displayDeletedShop', false) && $selectedShop->isDeleted()) {
$shops = $em $shops = $em->getRepository(
->getRepository('PSC\Shop\EntityBundle\Entity\ShopContact')->myEditableShopsWithNoDeleted($security->getUser()); 'PSC\Shop\EntityBundle\Entity\ShopContact',
)->myEditableShopsWithNoDeleted($security->getUser());
$request->getSession()->set('selectedShop', $shops[0]->getUid()); $request->getSession()->set('selectedShop', $shops[0]->getUid());
} }
$request->getSession()->set('displayDeletedShop', !$request->getSession()->get('displayDeletedShop', false)); $request->getSession()->set('displayDeletedShop', !$request->getSession()->get('displayDeletedShop', false));

View File

@ -11,17 +11,17 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class IconButtonExtension extends AbstractTypeExtension class IconButtonExtension extends AbstractTypeExtension
{ {
public function buildView(FormView $view, FormInterface $form, array $options) public function buildView(FormView $view, FormInterface $form, array $options): void
{ {
$view->vars['icon_before'] = $options['icon_before'] ? $options['icon_before'] : ''; $view->vars['icon_before'] = $options['icon_before'] ? $options['icon_before'] : '';
$view->vars['icon_after'] = $options['icon_after'] ? $options['icon_after'] : ''; $view->vars['icon_after'] = $options['icon_after'] ? $options['icon_after'] : '';
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver): void
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'icon_before' => null, 'icon_before' => null,
'icon_after' => null 'icon_after' => null,
]); ]);
} }

View File

@ -1,4 +1,4 @@
psc_backend_dashboard: psc_backend_dashboard:
resource: "@PSCBackendDashboardBundle/Controller/" resource: "@PSCBackendDashboardBundle/Controller/"
type: annotation type: attribute
prefix: /backend prefix: /backend

View File

@ -136,6 +136,122 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if instance.aiProvider %}
<div class="col-span-full"
x-data="{
message: '',
history: [],
loading: false,
error: '',
chatUrl: '{{ path("psc_component_ai_chat") }}',
async send() {
if (!this.message.trim() || this.loading) return;
const userMsg = this.message.trim();
this.message = '';
this.error = '';
this.history.push({ role: 'user', content: userMsg });
this.loading = true;
this.$nextTick(() => {
const el = this.$refs.messages;
el.scrollTop = el.scrollHeight;
});
try {
const res = await fetch(this.chatUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: userMsg, history: this.history.slice(0, -1) })
});
const data = await res.json();
if (data.error) {
this.error = data.error;
this.history.pop();
} else {
this.history.push({ role: 'assistant', content: data.reply });
}
} catch (e) {
this.error = 'Verbindungsfehler: ' + e.message;
this.history.pop();
} finally {
this.loading = false;
this.$nextTick(() => {
const el = this.$refs.messages;
el.scrollTop = el.scrollHeight;
});
}
}
}">
<div class="rounded-sm border border bg-white px-7.5 py-6 shadow-lg dark:border-strokedark dark:bg-boxdark">
<div class="p-2 space-y-2">
<div class="flex items-center justify-between gap-8 px-4 py-2 mb-2">
<h2 class="text-xl font-medium tracking-tight filament-card-heading flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-psc-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09Z" />
</svg>
KI Assistent
<span class="text-xs font-normal text-gray-400">({{ instance.aiProvider }}{% if instance.aiModel %} · {{ instance.aiModel }}{% endif %})</span>
</h2>
</div>
<div aria-hidden="true" class="filament-hr border-t dark:border-gray-700"></div>
</div>
{# Message history #}
<div x-ref="messages" class="h-80 overflow-y-auto px-4 py-2 space-y-3">
<template x-if="history.length === 0">
<p class="text-sm text-gray-400 text-center mt-8">Stellen Sie eine Frage oder geben Sie einen Befehl ein...</p>
</template>
<template x-for="(msg, idx) in history" :key="idx">
<div :class="msg.role === 'user' ? 'flex justify-end' : 'flex justify-start'">
<div :class="msg.role === 'user'
? 'bg-psc-500 text-white rounded-2xl rounded-tr-sm px-4 py-2 max-w-[75%] text-sm'
: 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-2xl rounded-tl-sm px-4 py-2 max-w-[75%] text-sm whitespace-pre-wrap'">
<span x-text="msg.content"></span>
</div>
</div>
</template>
<template x-if="loading">
<div class="flex justify-start">
<div class="bg-gray-100 dark:bg-gray-700 rounded-2xl rounded-tl-sm px-4 py-3 text-sm text-gray-500">
<span class="inline-flex gap-1">
<span class="w-1.5 h-1.5 bg-gray-400 rounded-full animate-bounce" style="animation-delay:0ms"></span>
<span class="w-1.5 h-1.5 bg-gray-400 rounded-full animate-bounce" style="animation-delay:150ms"></span>
<span class="w-1.5 h-1.5 bg-gray-400 rounded-full animate-bounce" style="animation-delay:300ms"></span>
</span>
</div>
</div>
</template>
</div>
{# Error #}
<template x-if="error">
<div class="mx-4 mb-2 px-3 py-2 bg-red-50 border border-red-200 rounded-md text-sm text-red-700" x-text="error"></div>
</template>
{# Input #}
<div class="px-4 pt-2 pb-2">
<div class="flex gap-2">
<input
type="text"
x-model="message"
@keydown.enter.prevent="send()"
:disabled="loading"
placeholder="Nachricht eingeben..."
class="flex-1 rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-psc-500 focus:border-transparent disabled:opacity-50"
>
<button
@click="send()"
:disabled="loading || !message.trim()"
class="inline-flex items-center gap-1.5 px-4 py-2 bg-psc-500 hover:bg-psc-600 disabled:opacity-50 disabled:cursor-not-allowed text-white text-sm font-medium rounded-lg transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
</svg>
Senden
</button>
</div>
</div>
</div>
</div>
{% endif %}
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -30,9 +30,9 @@ use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
@ -65,8 +65,8 @@ class ListController extends \Symfony\Bundle\FrameworkBundle\Controller\Abstract
* @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\ORMException
*/ */
#[Route(path: '/list/index', name: 'psc_backend_domain_list_index')] #[Route(path: '/list/index', name: 'psc_backend_domain_list_index')]
#[Template] #[Template('@PSCBackendDomain/backend/list/index.html.twig')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function indexAction(Request $request, Shop $shopService, DocumentManager $mongoService, EntityManagerInterface $em, DomainSync $sync) public function indexAction(Request $request, Shop $shopService, DocumentManager $mongoService, EntityManagerInterface $em, DomainSync $sync)
{ {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */
@ -101,7 +101,7 @@ class ListController extends \Symfony\Bundle\FrameworkBundle\Controller\Abstract
* *
*/ */
#[Route(path: '/list/sync', name: 'psc_backend_domain_list_sync')] #[Route(path: '/list/sync', name: 'psc_backend_domain_list_sync')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function syncAction(DomainSync $sync) public function syncAction(DomainSync $sync)
{ {
$sync->syncDomains(); $sync->syncDomains();
@ -120,7 +120,7 @@ class ListController extends \Symfony\Bundle\FrameworkBundle\Controller\Abstract
* @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\ORMException
*/ */
#[Route(path: '/list/delete/{uid}', name: 'psc_backend_domain_list_delete')] #[Route(path: '/list/delete/{uid}', name: 'psc_backend_domain_list_delete')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function deleteAction(Shop $shopService, EntityManagerInterface $entityManager, DocumentManager $mongoManager, $uid) public function deleteAction(Shop $shopService, EntityManagerInterface $entityManager, DocumentManager $mongoManager, $uid)
{ {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */

View File

@ -1,4 +1,4 @@
psc_backend_domain: psc_backend_domain:
resource: "@PSCBackendDomainBundle/Controller/Backend" resource: "@PSCBackendDomainBundle/Controller/Backend"
type: annotation type: attribute
prefix: /backend/domain prefix: /backend/domain

View File

@ -4,9 +4,9 @@ namespace PSC\Backend\ToolsBundle\Controller\Backend;
use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface; use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface;
use PSC\Backend\ToolsBundle\Service\ExporterRegistry; use PSC\Backend\ToolsBundle\Service\ExporterRegistry;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -25,9 +25,9 @@ class ExporterController extends AbstractController
* @param ExporterRegistry $registry * @param ExporterRegistry $registry
* @return array * @return array
*/ */
#[Template] #[Template('@PSCBackendTools/backend/exporter/list.html.twig')]
#[Route(path: '/list', name: 'psc_backend_tools_exporter_list_index')] #[Route(path: '/list', name: 'psc_backend_tools_exporter_list_index')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function listAction(ExporterRegistry $registry) public function listAction(ExporterRegistry $registry)
{ {
return array('exporters' => $registry->all()); return array('exporters' => $registry->all());
@ -41,8 +41,8 @@ class ExporterController extends AbstractController
* @return array * @return array
*/ */
#[Route(path: '/{type}/export', name: 'psc_backend_tools_exporter_form')] #[Route(path: '/{type}/export', name: 'psc_backend_tools_exporter_form')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
#[Template] #[Template('@PSCBackendTools/backend/exporter/form.html.twig')]
public function formAction(Request $request, ExporterRegistry $registry, $type) public function formAction(Request $request, ExporterRegistry $registry, $type)
{ {
if (!$registry->has($type)) { if (!$registry->has($type)) {

View File

@ -5,9 +5,9 @@ namespace PSC\Backend\ToolsBundle\Controller\Backend;
use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface; use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface;
use PSC\Backend\ToolsBundle\Service\ImporterRegistry; use PSC\Backend\ToolsBundle\Service\ImporterRegistry;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -23,9 +23,9 @@ class ImporterController extends AbstractController
dump($importer);die(); dump($importer);die();
} }
#[Template] #[Template('@PSCBackendTools/backend/importer/list.html.twig')]
#[Route(path: '/list', name: 'psc_backend_tools_importer_list_index')] #[Route(path: '/list', name: 'psc_backend_tools_importer_list_index')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function listAction(ImporterRegistry $registry) public function listAction(ImporterRegistry $registry)
{ {
return array('exporters' => $registry->all()); return array('exporters' => $registry->all());

View File

@ -8,6 +8,7 @@ use LogicException;
use Port\Csv\CsvWriter; use Port\Csv\CsvWriter;
use Port\Reader\ArrayReader; use Port\Reader\ArrayReader;
use PSC\Backend\ToolsBundle\Export\Writer\ExcelWriter; use PSC\Backend\ToolsBundle\Export\Writer\ExcelWriter;
use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface;
use PSC\Backend\ToolsBundle\Interfaces\ExporterInterface; use PSC\Backend\ToolsBundle\Interfaces\ExporterInterface;
use PSC\Backend\ToolsBundle\Service\ExporterRegistry; use PSC\Backend\ToolsBundle\Service\ExporterRegistry;
use PSC\Shop\EntityBundle\Entity\Contact; use PSC\Shop\EntityBundle\Entity\Contact;
@ -22,15 +23,19 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Validator\Constraints\Date; use Symfony\Component\Validator\Constraints\Date;
use Symfony\Component\Validator\Constraints\DateTime; use Symfony\Component\Validator\Constraints\DateTime;
class ContactExporter implements ExporterInterface class ContactExporter implements ExporterInterface, ConfigurableElementInterface
{ {
private $_formFactory = null; private $_formFactory = null;
private $_entityManager = null; private $_entityManager = null;
private $_shopService = null; private $_shopService = null;
/** @var Form */ /** @var Form */
private $_form = null; private $_form = null;
public function __construct(FormFactoryInterface $formFactory, EntityManagerInterface $entityManager, Shop $shopService)
{ public function __construct(
FormFactoryInterface $formFactory,
EntityManagerInterface $entityManager,
Shop $shopService,
) {
$this->_formFactory = $formFactory; $this->_formFactory = $formFactory;
$this->_entityManager = $entityManager; $this->_entityManager = $entityManager;
$this->_shopService = $shopService; $this->_shopService = $shopService;
@ -51,6 +56,11 @@ class ContactExporter implements ExporterInterface
public function getForm(FormBuilderInterface $builder, $form_options) public function getForm(FormBuilderInterface $builder, $form_options)
{ {
$builder->add('fotos', CheckboxType::class, ['required' => false, 'label' => 'Fotos mit exportieren?']);
$builder->add('fotosFolder', TextType::class, [
'required' => false,
'label' => 'Pfad für die Fotos z.B.: /data/www/old/data/packages/fotos/{{ media.uuid }}.{{media.extension}}',
]);
} }
public function getGroup() public function getGroup()
@ -68,9 +78,12 @@ class ContactExporter implements ExporterInterface
*/ */
public function export() public function export()
{ {
$rows = $this->_entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Contact')->getContactsByShop($this->_shopService->getSelectedShop()); $formData = $this->_form->getData();
$temp = array(); $rows = $this->_entityManager
$temp[] = array( ->getRepository('PSC\Shop\EntityBundle\Entity\Contact')
->getContactsByShop($this->_shopService->getSelectedShop());
$temp = [];
$temp[] = [
'id' => 'id', 'id' => 'id',
'uid' => 'uid', 'uid' => 'uid',
'account_id' => 'account_id', 'account_id' => 'account_id',
@ -108,16 +121,18 @@ class ContactExporter implements ExporterInterface
'alternativAppendix' => 'alternativAppendix', 'alternativAppendix' => 'alternativAppendix',
'alternativ' => 'alternativ', 'alternativ' => 'alternativ',
'alternativType' => 'alternativType', 'alternativType' => 'alternativType',
'birthday' => 'birthday' 'birthday' => 'birthday',
); 'image1' => 'image1',
'image2' => 'image2',
];
foreach ($rows as $row) { foreach ($rows as $row) {
/** @var Contact $contact */ /** @var Contact $contact */
$contact = $row->getContact(); $contact = $row->getContact();
$temp[] = array( $temp[] = [
'id' => $contact->getId(), 'id' => $contact->getId(),
'uid' => $contact->getId(), 'uid' => $contact->getId(),
'account_id' => ($contact->getAccount() ? (string)$contact->getAccount()->getId() : 0), 'account_id' => $contact->getAccount() ? ((string) $contact->getAccount()->getId()) : 0,
'account_title' => ($contact->getAccount() ? (string)$contact->getAccount()->getTitle() : 0), 'account_title' => $contact->getAccount() ? ((string) $contact->getAccount()->getTitle()) : 0,
'enable' => (int) $contact->isEnable(), 'enable' => (int) $contact->isEnable(),
'locked' => (int) $contact->isLocked(), 'locked' => (int) $contact->isLocked(),
'virtual' => (int) $contact->isVirtual(), 'virtual' => (int) $contact->isVirtual(),
@ -151,15 +166,22 @@ class ContactExporter implements ExporterInterface
'alternativAppendix' => $contact->getAlternativAppendix(), 'alternativAppendix' => $contact->getAlternativAppendix(),
'alternativ' => $contact->getAlternativ(), 'alternativ' => $contact->getAlternativ(),
'alternativType' => $contact->getAlternativType(), 'alternativType' => $contact->getAlternativType(),
'birthday' => $contact->getBirthday() 'birthday' => $contact->getBirthday(),
); 'image1' => $contact->getImage1() ?? '',
'image2' => $contact->getImage2() ?? '',
];
if ($formData['fotos'] && $contact->getImage1() != '') {
$this->mediaManager->copyMediaToFolder($contact->getImage1(), $formData['fotosFolder']);
}
if ($formData['fotos'] && $contact->getImage2() != '') {
$this->mediaManager->copyMediaToFolder($contact->getImage2(), $formData['fotosFolder']);
}
} }
$reader = new ArrayReader($temp); $reader = new ArrayReader($temp);
$writer = new CsvWriter(); $writer = new CsvWriter();
$writer->setStream(fopen('php://output', 'w')); $writer->setStream(fopen('php://output', 'w'));
$response = new StreamedResponse(function () use ($reader, $writer) { $response = new StreamedResponse(function () use ($reader, $writer) {
foreach ($reader as $row) { foreach ($reader as $row) {
$writer->writeItem($row); $writer->writeItem($row);
} }

View File

@ -4,19 +4,25 @@ namespace PSC\Backend\ToolsBundle\Exporter\Excel;
use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use MongoDB\BSON\ObjectId;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface;
use PSC\Backend\ToolsBundle\Interfaces\ExporterInterface; use PSC\Backend\ToolsBundle\Interfaces\ExporterInterface;
use PSC\Backend\ToolsBundle\Service\ExporterRegistry; use PSC\Backend\ToolsBundle\Service\ExporterRegistry;
use PSC\Shop\EntityBundle\Document\Contact as PSCContact; use PSC\Shop\EntityBundle\Document\Contact as PSCContact;
use PSC\Shop\EntityBundle\Entity\Contact; use PSC\Shop\EntityBundle\Entity\Contact;
use PSC\Shop\MediaBundle\Document\Media;
use PSC\Shop\MediaBundle\Service\MediaManager;
use PSC\System\SettingsBundle\Service\Shop; use PSC\System\SettingsBundle\Service\Shop;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Form; use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse;
class ContactExporter implements ExporterInterface class ContactExporter implements ExporterInterface, ConfigurableElementInterface
{ {
/** @var Form */ /** @var Form */
private $_form = null; private $_form = null;
@ -25,6 +31,7 @@ class ContactExporter implements ExporterInterface
private FormFactoryInterface $formFactory, private FormFactoryInterface $formFactory,
private EntityManagerInterface $entityManager, private EntityManagerInterface $entityManager,
private DocumentManager $mongoManager, private DocumentManager $mongoManager,
private MediaManager $mediaManager,
private Shop $shopService, private Shop $shopService,
) {} ) {}
@ -43,6 +50,11 @@ class ContactExporter implements ExporterInterface
public function getForm(FormBuilderInterface $builder, $form_options) public function getForm(FormBuilderInterface $builder, $form_options)
{ {
$builder->add('fotos', CheckboxType::class, ['required' => false, 'label' => 'Fotos mit exportieren?']);
$builder->add('fotosFolder', TextType::class, [
'required' => false,
'label' => 'Pfad für die Fotos z.B.: /data/www/old/data/packages/fotos/{{ media.uuid }}.{{media.extension}}',
]);
} }
public function getGroup() public function getGroup()
@ -60,6 +72,7 @@ class ContactExporter implements ExporterInterface
*/ */
public function export() public function export()
{ {
$formData = $this->_form->getData();
$rows = $this->entityManager $rows = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Contact') ->getRepository('PSC\Shop\EntityBundle\Entity\Contact')
->getContactsByShop($this->shopService->getSelectedShop()); ->getContactsByShop($this->shopService->getSelectedShop());
@ -128,6 +141,8 @@ class ContactExporter implements ExporterInterface
'custom22' => $shop->getCustomerFieldName22(), 'custom22' => $shop->getCustomerFieldName22(),
'custom23' => $shop->getCustomerFieldName23(), 'custom23' => $shop->getCustomerFieldName23(),
'custom24' => $shop->getCustomerFieldName24(), 'custom24' => $shop->getCustomerFieldName24(),
'image1' => 'image1',
'image2' => 'image2',
]; ];
foreach ($rows as $row) { foreach ($rows as $row) {
/** @var Contact $contact */ /** @var Contact $contact */
@ -200,9 +215,17 @@ class ContactExporter implements ExporterInterface
'custom22' => $contactDoc ? $contactDoc->getCustom22() : '', 'custom22' => $contactDoc ? $contactDoc->getCustom22() : '',
'custom23' => $contactDoc ? $contactDoc->getCustom23() : '', 'custom23' => $contactDoc ? $contactDoc->getCustom23() : '',
'custom24' => $contactDoc ? $contactDoc->getCustom24() : '', 'custom24' => $contactDoc ? $contactDoc->getCustom24() : '',
'image1' => $contact->getImage1() ?? '',
'image2' => $contact->getImage2() ?? '',
]; ];
}
if ($formData['fotos'] && $contact->getImage1() != '') {
$this->mediaManager->copyMediaToFolder($contact->getImage1(), $formData['fotosFolder']);
}
if ($formData['fotos'] && $contact->getImage2() != '') {
$this->mediaManager->copyMediaToFolder($contact->getImage2(), $formData['fotosFolder']);
}
}
$spreadsheet = new Spreadsheet(); $spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet()->fromArray( $spreadsheet->getActiveSheet()->fromArray(

View File

@ -2,15 +2,15 @@
namespace PSC\Backend\ToolsBundle\Exporter\Excel; namespace PSC\Backend\ToolsBundle\Exporter\Excel;
require_once(__DIR__ . '/../../../../Shop/EntityBundle/Lagacy/TP_Basket_Item.php'); require_once __DIR__ . '/../../../../Shop/EntityBundle/Lagacy/TP_Basket_Item.php';
use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface;
use PSC\Backend\ToolsBundle\Interfaces\ExporterInterface; use PSC\Backend\ToolsBundle\Interfaces\ExporterInterface;
use PSC\Backend\ToolsBundle\Service\ExporterRegistry; use PSC\Backend\ToolsBundle\Service\ExporterRegistry;
use PSC\Backend\ToolsBundle\Interfaces\ConfigurableElementInterface;
use PSC\Library\Calc\Engine; use PSC\Library\Calc\Engine;
use PSC\Library\Calc\PaperContainer; use PSC\Library\Calc\PaperContainer;
use PSC\Shop\EntityBundle\Document\Position; use PSC\Shop\EntityBundle\Document\Position;
@ -25,6 +25,7 @@ use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\BinaryFileResponse;
class PositionExporter implements ExporterInterface, ConfigurableElementInterface class PositionExporter implements ExporterInterface, ConfigurableElementInterface
{ {
private $_formFactory = null; private $_formFactory = null;
@ -41,8 +42,14 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
* @var DocumentManager * @var DocumentManager
*/ */
private DocumentManager $_mongoDb; private DocumentManager $_mongoDb;
function __construct(FormFactoryInterface $formFactory, EntityManagerInterface $entityManager, Shop $shopService, PaperDB $paperDbService, DocumentManager $mongodb)
{ function __construct(
FormFactoryInterface $formFactory,
EntityManagerInterface $entityManager,
Shop $shopService,
PaperDB $paperDbService,
DocumentManager $mongodb,
) {
$this->_formFactory = $formFactory; $this->_formFactory = $formFactory;
$this->_entityManager = $entityManager; $this->_entityManager = $entityManager;
$this->_shopService = $shopService; $this->_shopService = $shopService;
@ -68,8 +75,8 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
$builder->add('product', EntityType::class, array( $builder->add('product', EntityType::class, array(
'class' => Product::class, 'class' => Product::class,
'query_builder' => function (EntityRepository $er) { 'query_builder' => function (EntityRepository $er) {
return $er
return $er->createQueryBuilder('g') ->createQueryBuilder('g')
->andWhere('g.shop = :shop and g.originalProduct = 0') ->andWhere('g.shop = :shop and g.originalProduct = 0')
->setParameter('shop', $this->_shopService->getSelectedShop()->getUid()) ->setParameter('shop', $this->_shopService->getSelectedShop()->getUid())
->orderBy('g.title', 'ASC'); ->orderBy('g.title', 'ASC');
@ -77,11 +84,15 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
'choice_label' => 'title', 'choice_label' => 'title',
'required' => false, 'required' => false,
'placeholder' => 'Alle', 'placeholder' => 'Alle',
'label' => 'Vorgänger' 'label' => 'Vorgänger',
));
$builder->add('from', DateType::class, array('label' => 'Von', 'attr' => array('class' => 'form-element')));
$builder->add('to', DateType::class, array('label' => 'Bis', 'attr' => array('class' => 'form-element')));
$builder->add('calc_options', TextType::class, array(
'label' => 'Kalkulationsoptionen (auflage,papier)',
'required' => false,
'attr' => array('class' => 'form-element'),
)); ));
$builder->add("from", DateType::class, array('label' => 'Von', 'attr' => array('class' => 'form-element')));
$builder->add("to", DateType::class, array('label' => 'Bis', 'attr' => array('class' => 'form-element')));
$builder->add("calc_options", TextType::class, array('label' => 'Kalkulationsoptionen (auflage,papier)', 'required' => false, 'attr' => array('class' => 'form-element')));
} }
public function getGroup() public function getGroup()
@ -102,15 +113,17 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
$formData = $this->_form->getData(); $formData = $this->_form->getData();
$orderRepository = $this->_entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Orderpos'); $orderRepository = $this->_entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Orderpos');
if ($formData['product'] === null) { if ($formData['product'] === null) {
$qb = $orderRepository->createQueryBuilder('o') $qb = $orderRepository
->where("o.createdDate >= :from and o.createdDate <= :to and o.shop = :shop") ->createQueryBuilder('o')
->where('o.createdDate >= :from and o.createdDate <= :to and o.shop = :shop')
->setParameter('from', $formData['from']) ->setParameter('from', $formData['from'])
->setParameter('to', $formData['to']) ->setParameter('to', $formData['to'])
->setParameter('shop', $this->_shopService->getSelectedShop()->getId()) ->setParameter('shop', $this->_shopService->getSelectedShop()->getId())
->getQuery(); ->getQuery();
} else { } else {
$qb = $orderRepository->createQueryBuilder('o') $qb = $orderRepository
->where("o.createdDate >= :from and o.createdDate <= :to and o.shop = :shop AND o.product = :product") ->createQueryBuilder('o')
->where('o.createdDate >= :from and o.createdDate <= :to and o.shop = :shop AND o.product = :product')
->setParameter('from', $formData['from']) ->setParameter('from', $formData['from'])
->setParameter('to', $formData['to']) ->setParameter('to', $formData['to'])
->setParameter('shop', $this->_shopService->getSelectedShop()->getId()) ->setParameter('shop', $this->_shopService->getSelectedShop()->getId())
@ -119,7 +132,7 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
} }
$rows = $qb->getResult(); $rows = $qb->getResult();
$calcOptions = explode(",", $formData['calc_options']); $calcOptions = explode(',', $formData['calc_options']);
$data[] = array(); $data[] = array();
$temp = array( $temp = array(
'order' => 'order', 'order' => 'order',
@ -144,7 +157,7 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
'productId' => 'articleId', 'productId' => 'articleId',
'productTitle' => 'articleName', 'productTitle' => 'articleName',
'weight' => 'weight', 'weight' => 'weight',
'articleName' => 'articleName' 'articleName' => 'articleName',
); );
foreach ($calcOptions as $key) { foreach ($calcOptions as $key) {
$temp[$key] = $key; $temp[$key] = $key;
@ -152,20 +165,26 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
$data[] = $temp; $data[] = $temp;
$paperContainer = new PaperContainer(); $paperContainer = new PaperContainer();
$paperContainer->parse(simplexml_load_string($this->_shopService->getSelectedShop()->getInstall()->getPaperContainer())); $paperContainer->parse(simplexml_load_string(
$this->_shopService
->getSelectedShop()
->getInstall()
->getPaperContainer(),
));
$engine = new Engine(); $engine = new Engine();
$engine->setPaperRepository($this->_paperDbService); $engine->setPaperRepository($this->_paperDbService);
$engine->setPaperContainer($paperContainer); $engine->setPaperContainer($paperContainer);
if ($this->_shopService->getSelectedShop()->getInstall()->getCalcTemplates()) { if ($this->_shopService->getSelectedShop()->getInstall()->getCalcTemplates()) {
$engine->setTemplates('<root>' . $this->_shopService->getSelectedShop()->getInstall()->getCalcTemplates() . '</root>'); $engine->setTemplates('<root>' . $this->_shopService
->getSelectedShop()
->getInstall()
->getCalcTemplates() . '</root>');
} }
/** @var Orderpos $row */ /** @var Orderpos $row */
foreach ($rows as $row) { foreach ($rows as $row) {
/** @var Position $objDoc */ /** @var Position $objDoc */
$objDoc = $this->_mongoDb $objDoc = $this->_mongoDb->getRepository(Position::class)->findOneBy(['uid' => (string) $row->getId()]);
->getRepository(Position::class)
->findOneBy(['uid' => (string)$row->getId()]);
$temp = array( $temp = array(
'order' => (string) $row->getOrder()->getAlias(), 'order' => (string) $row->getOrder()->getAlias(),
'pos' => (string) $row->getPos(), 'pos' => (string) $row->getPos(),
@ -190,7 +209,7 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
'productTitle' => (string) $row->getProduct()->getTitle(), 'productTitle' => (string) $row->getProduct()->getTitle(),
'weight' => (string) $row->getWeight(), 'weight' => (string) $row->getWeight(),
); );
$objPosition = unserialize(($row->getData())); $objPosition = unserialize($row->getData());
if ($row->hasCalcXml()) { if ($row->hasCalcXml()) {
$engine->loadString($row->getCalcXml()); $engine->loadString($row->getCalcXml());
$engine->setVariables($objPosition->getOptions()); $engine->setVariables($objPosition->getOptions());
@ -207,7 +226,7 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
$articleCalc = new \PSC\Library\Calc\Article($objPosition->getOptions()['kalk_artikel']); $articleCalc = new \PSC\Library\Calc\Article($objPosition->getOptions()['kalk_artikel']);
$temp['articleName'] = $objPosition->getOptions()['kalk_artikel']; $temp['articleName'] = $objPosition->getOptions()['kalk_artikel'];
} else { } else {
$articleCalc = new \PSC\Library\Calc\Article("test"); $articleCalc = new \PSC\Library\Calc\Article('test');
$temp['articleName'] = ''; $temp['articleName'] = '';
} }
} }
@ -216,7 +235,7 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
if ($articleCalc->getOptionById($key)) { if ($articleCalc->getOptionById($key)) {
$temp[$key] = (string) $articleCalc->getOptionById($key)->getValue(); $temp[$key] = (string) $articleCalc->getOptionById($key)->getValue();
} else { } else {
$temp[$key] = ""; $temp[$key] = '';
} }
} }
@ -225,11 +244,10 @@ class PositionExporter implements ExporterInterface, ConfigurableElementInterfac
$spreadsheet = new Spreadsheet(); $spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet() $spreadsheet->getActiveSheet()->fromArray(
->fromArray(
$data, // The data to set $data, // The data to set
NULL, // Array values with this value will not be set null, // Array values with this value will not be set
'A1' // Top left coordinate of the worksheet range where 'A1', // Top left coordinate of the worksheet range where
// we want to set these values (default is A1) // we want to set these values (default is A1)
); );

View File

@ -1,6 +1,6 @@
psc_backend_tools: psc_backend_tools:
resource: "@PSCBackendToolsBundle/Controller/Backend" resource: "@PSCBackendToolsBundle/Controller/Backend"
type: annotation type: attribute
prefix: /backend/tools prefix: /backend/tools

View File

@ -0,0 +1,34 @@
<?php
namespace PSC\Component\AiBundle\Controller;
use PSC\Component\AiBundle\Service\AiAgentService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class AiChatController extends AbstractController
{
#[IsGranted('ROLE_SHOP')]
#[Route(path: '/ai/chat', name: 'psc_component_ai_chat', methods: ['POST'])]
public function chat(Request $request, AiAgentService $agentService): JsonResponse
{
$data = json_decode($request->getContent(), true);
$message = trim($data['message'] ?? '');
$history = $data['history'] ?? [];
if (empty($message)) {
return new JsonResponse(['error' => 'Nachricht ist leer'], Response::HTTP_BAD_REQUEST);
}
try {
$reply = $agentService->chat($message, $history);
return new JsonResponse(['reply' => $reply]);
} catch (\Exception $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace PSC\Component\AiBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('psc_component_ai');
return $treeBuilder;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace PSC\Component\AiBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class PSCComponentAiExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace PSC\Component\AiBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class PSCComponentAiBundle extends Bundle {}

View File

@ -0,0 +1,85 @@
<?php
namespace PSC\Component\AiBundle\Platform\Completions;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\AI\Platform\Capability;
use Symfony\AI\Platform\Model;
use Symfony\AI\Platform\ModelClientInterface;
use Symfony\AI\Platform\Result\RawHttpResult;
use Symfony\AI\Platform\Result\RawResultInterface;
use Symfony\Component\HttpClient\EventSourceHttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* OpenAI-compatible chat completions client.
* Works with Ollama, OpenAI, Mistral, Azure OpenAI and any other OpenAI-compatible API.
*/
final class ModelClient implements ModelClientInterface
{
private readonly EventSourceHttpClient $httpClient;
public function __construct(
HttpClientInterface $httpClient,
private readonly string $baseUrl,
private readonly ?string $apiKey = null,
private readonly string $path = '/v1/chat/completions',
private readonly LoggerInterface $logger = new NullLogger(),
) {
$this->httpClient = $httpClient instanceof EventSourceHttpClient
? $httpClient
: new EventSourceHttpClient($httpClient);
}
public function supports(Model $model): bool
{
return $model->supports(Capability::INPUT_MESSAGES)
|| $model->supports(Capability::TOOL_CALLING);
}
/**
* @param array<string|int, mixed> $payload
* @param array<string, mixed> $options
*/
public function request(Model $model, array|string $payload, array $options = []): RawResultInterface
{
$headers = ['Content-Type' => 'application/json'];
if ($this->apiKey) {
$headers['Authorization'] = 'Bearer ' . $this->apiKey;
}
$body = array_merge($payload, ['model' => $model->getName()]);
// Tools in OpenAI-Format bringen: {type: "function", function: {name, description, parameters}}
if (!empty($options['tools'])) {
$body['tools'] = array_map(
static function (array $tool): array {
// Contract liefert flache Objekte {name, description, parameters}
// OpenAI erwartet {type: "function", function: {...}}
if (!isset($tool['type'])) {
return ['type' => 'function', 'function' => $tool];
}
return $tool;
},
$options['tools']
);
$body['tool_choice'] = 'auto';
}
$this->logger->debug('AI request body', [
'url' => rtrim($this->baseUrl, '/') . $this->path,
'model' => $model->getName(),
'tools' => array_column($body['tools'] ?? [], 'function'),
'messages' => count($body['messages'] ?? []),
]);
$response = $this->httpClient->request('POST', rtrim($this->baseUrl, '/') . $this->path, [
'headers' => $headers,
'json' => $body,
'timeout' => 120,
]);
return new RawHttpResult($response);
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace PSC\Component\AiBundle\Platform\Completions;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\AI\Platform\Capability;
use Symfony\AI\Platform\Model;
use Symfony\AI\Platform\Result\RawResultInterface;
use Symfony\AI\Platform\Result\ResultInterface;
use Symfony\AI\Platform\Result\TextResult;
use Symfony\AI\Platform\Result\ToolCall;
use Symfony\AI\Platform\Result\ToolCallResult;
use Symfony\AI\Platform\ResultConverterInterface;
use Symfony\AI\Platform\TokenUsage\TokenUsageExtractorInterface;
/**
* Parses OpenAI-compatible chat completions responses into TextResult or ToolCallResult.
*/
final class ResultConverter implements ResultConverterInterface
{
public function __construct(
private readonly LoggerInterface $logger = new NullLogger(),
) {
}
public function supports(Model $model): bool
{
return $model->supports(Capability::INPUT_MESSAGES)
|| $model->supports(Capability::TOOL_CALLING);
}
/**
* @param array<string, mixed> $options
*/
public function convert(RawResultInterface $result, array $options = []): ResultInterface
{
$data = $result->getData();
$choice = $data['choices'][0] ?? null;
$this->logger->debug('AI response received', [
'finish_reason' => $choice['finish_reason'] ?? null,
'has_tool_calls' => !empty($choice['message']['tool_calls']),
'content_preview' => substr((string) ($choice['message']['content'] ?? ''), 0, 150),
]);
if (!$choice) {
return new TextResult('');
}
$message = $choice['message'] ?? [];
// Tool calls zurückgeben wenn vorhanden
if (!empty($message['tool_calls'])) {
$toolCalls = [];
foreach ($message['tool_calls'] as $tc) {
$args = $tc['function']['arguments'] ?? '{}';
if (is_string($args)) {
$args = json_decode($args, true, flags: \JSON_THROW_ON_ERROR);
}
$toolCalls[] = new ToolCall(
id: $tc['id'],
name: $tc['function']['name'],
arguments: $args,
);
}
return new ToolCallResult(...$toolCalls);
}
$content = $message['content'] ?? '';
// Manche Modelle (z.B. Ollama mit reasoning) nutzen 'thinking' + 'content'
if (is_array($content)) {
$text = '';
foreach ($content as $part) {
if (($part['type'] ?? '') === 'text') {
$text .= $part['text'];
}
}
$content = $text;
}
return new TextResult((string) $content);
}
public function getTokenUsageExtractor(): ?TokenUsageExtractorInterface
{
return null;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace PSC\Component\AiBundle\Platform;
use PSC\Component\AiBundle\Platform\Completions\ModelClient;
use PSC\Component\AiBundle\Platform\Completions\ResultConverter;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\AI\Platform\ModelCatalog\FallbackModelCatalog;
use Symfony\AI\Platform\Platform;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* Erstellt eine Platform-Instanz für OpenAI-kompatible APIs.
* Unterstützt: Ollama, OpenAI, Mistral, Azure OpenAI und andere.
*/
final class PlatformFactory
{
public static function create(
string $baseUrl,
?string $apiKey = null,
?HttpClientInterface $httpClient = null,
string $completionsPath = '/v1/chat/completions',
LoggerInterface $logger = new NullLogger(),
): Platform {
$modelClient = new ModelClient($httpClient, $baseUrl, $apiKey, $completionsPath, $logger);
$resultConverter = new ResultConverter($logger);
return new Platform(
modelClients: [$modelClient],
resultConverters: [$resultConverter],
modelCatalog: new FallbackModelCatalog(),
);
}
}

View File

@ -0,0 +1,4 @@
psc_component_ai:
resource: "@PSCComponentAiBundle/Controller/"
type: attribute
prefix: /backend

View File

@ -0,0 +1,7 @@
services:
_defaults:
autowire: true
autoconfigure: true
PSC\Component\AiBundle\:
resource: '../../*/*'

View File

@ -0,0 +1,140 @@
<?php
namespace PSC\Component\AiBundle\Service;
use PSC\Component\AiBundle\Platform\PlatformFactory;
use PSC\Shop\OrderBundle\Ai\Tool\ChangeOrderStatus;
use PSC\Shop\OrderBundle\Ai\Tool\GetAvailableOrderStatuses;
use PSC\Shop\OrderBundle\Ai\Tool\GetOrder;
use PSC\Shop\OrderBundle\Ai\Tool\SearchOrders;
use PSC\System\SettingsBundle\Service\Instance;
use PSC\System\SettingsBundle\Service\Status;
use Psr\Log\LoggerInterface;
use Symfony\AI\Agent\Agent;
use Symfony\AI\Agent\InputProcessor\SystemPromptInputProcessor;
use Symfony\AI\Agent\Toolbox\AgentProcessor;
use Symfony\AI\Agent\Toolbox\Toolbox;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;
use Symfony\AI\Platform\PlatformInterface;
use Symfony\AI\Platform\Result\TextResult;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class AiAgentService
{
public function __construct(
private readonly Instance $instanceService,
private readonly HttpClientInterface $httpClient,
private readonly GetOrder $getOrder,
private readonly SearchOrders $searchOrders,
private readonly ChangeOrderStatus $changeOrderStatus,
private readonly GetAvailableOrderStatuses $getAvailableOrderStatuses,
private readonly Status $statusService,
#[Autowire(service: 'monolog.logger.ai')]
private readonly LoggerInterface $logger,
) {}
/**
* @param array<array{role: string, content: string}> $history
*/
public function chat(string $message, array $history = []): string
{
$instance = $this->instanceService->getInstance();
$provider = $instance->getAiProvider();
if (!$provider) {
throw new \RuntimeException('Kein KI Anbieter konfiguriert.');
}
$platform = $this->createPlatform($instance);
$model = $instance->getAiModel() ?: 'llama3.2';
$this->logger->info('AI chat request', [
'provider' => $provider,
'model' => $model,
'message' => $message,
]);
$toolbox = new Toolbox(
tools: [
$this->getOrder,
$this->searchOrders,
$this->changeOrderStatus,
$this->getAvailableOrderStatuses,
],
logger: $this->logger,
);
$agentProcessor = new AgentProcessor($toolbox);
$systemPrompt = $this->buildSystemPrompt($instance);
$systemPromptProcessor = new SystemPromptInputProcessor($systemPrompt);
$agent = new Agent(
platform: $platform,
model: $model,
inputProcessors: [$systemPromptProcessor, $agentProcessor],
outputProcessors: [$agentProcessor],
);
$messages = new MessageBag();
foreach ($history as $h) {
if ($h['role'] === 'user') {
$messages->add(Message::ofUser($h['content']));
} elseif ($h['role'] === 'assistant') {
$messages->add(Message::ofAssistant($h['content']));
}
}
$messages->add(Message::ofUser($message));
$result = $agent->call($messages);
if ($result instanceof TextResult) {
return $result->getContent();
}
return (string) $result;
}
private function createPlatform($instance): PlatformInterface
{
$provider = $instance->getAiProvider();
$apiKey = $instance->getAiApiKey();
$baseUrl = $instance->getAiBaseUrl();
$baseUrl = match ($provider) {
'ollama' => rtrim($baseUrl ?: 'http://localhost:11434', '/'),
'openai' => 'https://api.openai.com',
'mistral' => 'https://api.mistral.ai',
'azure' => rtrim($baseUrl ?: '', '/'),
default => rtrim($baseUrl ?: 'http://localhost:11434', '/'),
};
return PlatformFactory::create(baseUrl: $baseUrl, apiKey: $apiKey ?: null, httpClient: $this->httpClient, logger: $this->logger);
}
private function buildSystemPrompt($instance): string
{
$shop = $this->instanceService->getShop();
$statusLines = [];
foreach ($this->statusService->getOrderStatuse() as $status) {
$statusLines[] = sprintf(' - Code %d: %s', $status->getCode(), $status->getInternalName());
}
$statusList = implode("\n", $statusLines);
return sprintf(
'Du bist ein KI-Assistent für das PrintshopCreator Backend des Shops "%s". '
. 'Du hilfst bei der Verwaltung von Aufträgen, Kunden und Produkten. '
. 'Antworte immer auf Deutsch. '
. 'Nutze die verfügbaren Tools um Daten abzurufen oder Änderungen vorzunehmen. '
. 'Fasse alle durchgeführten Aktionen am Ende kurz zusammen. '
. 'Das heutige Datum ist: %s.' . "\n\n"
. "Verfügbare Auftragsstatus:\n%s\n"
. 'Nutze beim Statuswechsel immer den Code aus dieser Liste, auch wenn der Nutzer den Namen nur ungefähr nennt.',
$shop->getTitle(),
date('d.m.Y'),
$statusList,
);
}
}

View File

@ -3,66 +3,65 @@
namespace PSC\Component\ApiBundle\Api\Account; namespace PSC\Component\ApiBundle\Api\Account;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\RequestBody;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Input; use PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Input;
use PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Output; use PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Output;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Component\ApiBundle\Dto\Shop\Shops; use PSC\Component\ApiBundle\Dto\Shop\Shops;
use PSC\Component\ApiBundle\Model\Shop; use PSC\Component\ApiBundle\Model\Shop;
use PSC\Component\ApiBundle\Model\Shop\Domain; use PSC\Component\ApiBundle\Model\Shop\Domain;
use PSC\Shop\EntityBundle\Entity\Account;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class GetParentByTitle extends AbstractController class GetParentByTitle extends AbstractController
{ {
private EntityManagerInterface $entityManager; private EntityManagerInterface $entityManager;
private \PSC\System\SettingsBundle\Service\Shop $shopService; private \PSC\System\SettingsBundle\Service\Shop $shopService;
public function __construct(EntityManagerInterface $entityManager, \PSC\System\SettingsBundle\Service\Shop $shopService) public function __construct(
{ EntityManagerInterface $entityManager,
\PSC\System\SettingsBundle\Service\Shop $shopService,
) {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->shopService = $shopService; $this->shopService = $shopService;
} }
/** #[Response(
* Check account parents by title response: 200,
* description: 'account',
* @OA\Response( content: new JsonContent(
* response=200, ref: new Model(type: PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Output::class),
* description="account", ),
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Output::class)) )]
* ) #[RequestBody(
* @OA\RequestBody( description: 'This is a request body',
* description="This is a request body", content: new Model(type: PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Input::class),
* @Model(type=\PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Input::class)) )]
* ) #[Tag(name: 'Account')]
* @OA\Tag(name="Account") #[IsGranted('ROLE_API')]
* @IsGranted("ROLE_API") #[Security(name: 'ApiKeyAuth')]
* @Security(name="ApiKeyAuth")
*/
#[Route(path: '/account/getparentbytitle', methods: ['POST'])] #[Route(path: '/account/getparentbytitle', methods: ['POST'])]
#[ParamConverter('data', class: '\PSC\Component\ApiBundle\Dto\Account\GetParentByTitle\Input', converter: 'psc_rest.request_body')] public function existsAction(#[MapRequestPayload] Input $data): JsonResponse
public function existsAction(Input $data): JsonResponse
{ {
$output = new Output(); $output = new Output();
$account = $this->entityManager $account = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Account') ->getRepository('PSC\Shop\EntityBundle\Entity\Account')
->getAccountByTitle( ->getAccountByTitle($data->title);
$data->title
);
if (!$account) { if (!$account) {
$output->parent = ""; $output->parent = '';
$output->parent_id = 0; $output->parent_id = 0;
return $this->json($output); return $this->json($output);
} }
@ -77,14 +76,7 @@ class GetParentByTitle extends AbstractController
return $this->json($output); return $this->json($output);
} }
/** protected function recursiveParent(\PSC\Shop\EntityBundle\Entity\Account $account): Account
* Gets the Parent Account
*
* @param \PSC\Shop\EntityBundle\Entity\Account $account
*
* @return \PSC\Shop\EntityBundle\Entity\Account
*/
protected function recursiveParent(\PSC\Shop\EntityBundle\Entity\Account $account)
{ {
if ($account->getParent()) { if ($account->getParent()) {
$account = $this->recursiveParent($account->getParent()); $account = $this->recursiveParent($account->getParent());

View File

@ -3,9 +3,11 @@
namespace PSC\Component\ApiBundle\Api\Cms; namespace PSC\Component\ApiBundle\Api\Cms;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Cms\Cms\Output; use PSC\Component\ApiBundle\Dto\Cms\Cms\Output;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Component\ApiBundle\Dto\Shop\Shops; use PSC\Component\ApiBundle\Dto\Shop\Shops;
@ -14,11 +16,11 @@ use PSC\Component\ApiBundle\Model\Shop;
use PSC\Component\ApiBundle\Model\Shop\Domain; use PSC\Component\ApiBundle\Model\Shop\Domain;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class Get extends AbstractController class Get extends AbstractController
{ {
@ -26,27 +28,24 @@ class Get extends AbstractController
private Cms $hydrateCms; private Cms $hydrateCms;
private \PSC\System\SettingsBundle\Service\Shop $shopService; private \PSC\System\SettingsBundle\Service\Shop $shopService;
public function __construct(EntityManagerInterface $entityManager, \PSC\System\SettingsBundle\Service\Shop $shopService, Cms $hydrateCms) public function __construct(
{ EntityManagerInterface $entityManager,
\PSC\System\SettingsBundle\Service\Shop $shopService,
Cms $hydrateCms,
) {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->hydrateCms = $hydrateCms; $this->hydrateCms = $hydrateCms;
$this->shopService = $shopService; $this->shopService = $shopService;
} }
/** #[Response(response: 200, description: 'cms', content: new JsonContent(ref: new Model(type: Output::class)))]
* Cms #[Tag(name: 'Cms')]
*
* @OA\Response(
* response=200,
* description="cms",
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Cms\Cms\Output::class))
* )
* @OA\Tag(name="Cms")
*/
#[Route(path: '/cms', methods: ['GET'])] #[Route(path: '/cms', methods: ['GET'])]
public function allAction(): JsonResponse public function allAction(): JsonResponse
{ {
$cmss = $this->entityManager->getRepository(\PSC\Shop\EntityBundle\Entity\Cms::class)->findAllByShop($this->shopService->getShopByDomain()); $cmss = $this->entityManager
->getRepository(\PSC\Shop\EntityBundle\Entity\Cms::class)
->findAllByShop($this->shopService->getShopByDomain());
$data = []; $data = [];
/** @var \PSC\Shop\EntityBundle\Entity\Cms $cms */ /** @var \PSC\Shop\EntityBundle\Entity\Cms $cms */
@ -57,21 +56,17 @@ class Get extends AbstractController
return $this->json(new Output($data)); return $this->json(new Output($data));
} }
/** #[Response(
* Get One Cms response: 200,
* description: 'found cms',
* @OA\Response( content: new JsonContent(ref: new Model(type: \PSC\Component\ApiBundle\Model\Cms::class)),
* response=200, )]
* description="found cms", #[Response(
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Model\Cms::class)) response: 404,
* ) description: 'not found',
* @OA\Response( content: new JsonContent(ref: new Model(type: NotFound::class)),
* response=404, )]
* description="not found", #[Tag(name: 'Cms')]
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Error\NotFound::class))
* )
* @OA\Tag(name="Cms")
*/
#[Route(path: '/cms/{id}', methods: ['GET'])] #[Route(path: '/cms/{id}', methods: ['GET'])]
public function one($id): JsonResponse public function one($id): JsonResponse
{ {

View File

@ -4,9 +4,11 @@ namespace PSC\Component\ApiBundle\Api\Plugin;
use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\DocumentManager;
use MongoDB\BSON\ObjectId; use MongoDB\BSON\ObjectId;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Component\ApiBundle\Dto\Shop\Shops; use PSC\Component\ApiBundle\Dto\Shop\Shops;
use PSC\Component\ApiBundle\Model\Shop; use PSC\Component\ApiBundle\Model\Shop;
@ -14,11 +16,11 @@ use PSC\Component\ApiBundle\Model\Shop\Domain;
use PSC\System\PluginBundle\Interfaces\Plugin; use PSC\System\PluginBundle\Interfaces\Plugin;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class Get extends AbstractController class Get extends AbstractController
{ {
@ -29,16 +31,12 @@ class Get extends AbstractController
$this->documentManager = $documentManager; $this->documentManager = $documentManager;
} }
/** #[Response(
* Layouter Plugins response: 200,
* description: 'shops',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Dto\Plugin\Plugins\Output::class)),
* response=200, )]
* description="shops", #[Tag(name: 'Plugin')]
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Plugin\Plugins\Output::class))
* )
* @OA\Tag(name="Plugin")
*/
#[Route(path: '/plugins/layouter', methods: ['GET'])] #[Route(path: '/plugins/layouter', methods: ['GET'])]
public function layouterAction(): JsonResponse public function layouterAction(): JsonResponse
{ {
@ -61,17 +59,13 @@ class Get extends AbstractController
return $this->json(new Shops\Output($data)); return $this->json(new Shops\Output($data));
} }
/** #[Response(
* Plugins response: 200,
* description: 'shops',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Dto\Plugin\Plugins\Output::class)),
* response=200, )]
* description="shops", #[Tag(name: 'Plugin')]
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Plugin\Plugins\Output::class)) #[Security(name: 'ApiKeyAuth')]
* )
* @OA\Tag(name="Plugin")
* @Security(name="ApiKeyAuth")
*/
#[Route(path: '/plugins', methods: ['GET'])] #[Route(path: '/plugins', methods: ['GET'])]
#[IsGranted('ROLE_API')] #[IsGranted('ROLE_API')]
public function allAction(): JsonResponse public function allAction(): JsonResponse
@ -90,27 +84,25 @@ class Get extends AbstractController
return $this->json(new Shops\Output($data)); return $this->json(new Shops\Output($data));
} }
/** #[Response(
* Get one plugin response: 200,
* description: 'found plugin',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Model\Plugin::class)),
* response=200, )]
* description="found plugin", #[Response(
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Model\Plugin::class)) response: 404,
* ) description: 'not found',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Dto\Error\NotFound::class)),
* response=404, )]
* description="not found", #[Tag(name: 'Plugin')]
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Error\NotFound::class)) #[Security(name: 'ApiKeyAuth')]
* )
* @OA\Tag(name="Plugin")
* @Security(name="ApiKeyAuth")
*/
#[Route(path: '/plugins/{uuid}', methods: ['GET'])] #[Route(path: '/plugins/{uuid}', methods: ['GET'])]
#[IsGranted('ROLE_API')] #[IsGranted('ROLE_API')]
public function one($uuid): JsonResponse public function one($uuid): JsonResponse
{ {
$plugin = $this->documentManager->getRepository(\PSC\System\PluginBundle\Document\Plugin::class)->findOneBy(['id' => new ObjectId($uuid)]); $plugin = $this->documentManager
->getRepository(\PSC\System\PluginBundle\Document\Plugin::class)
->findOneBy(['id' => new ObjectId($uuid)]);
if (!$plugin) { if (!$plugin) {
return $this->json(new NotFound()); return $this->json(new NotFound());
} }

View File

@ -3,44 +3,44 @@
namespace PSC\Component\ApiBundle\Api\Shop; namespace PSC\Component\ApiBundle\Api\Shop;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Component\ApiBundle\Dto\Shop\Shops; use PSC\Component\ApiBundle\Dto\Shop\Shops;
use PSC\Component\ApiBundle\Model\Shop; use PSC\Component\ApiBundle\Model\Shop;
use PSC\Component\ApiBundle\Model\Shop\Domain; use PSC\Component\ApiBundle\Model\Shop\Domain;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class Get extends AbstractController class Get extends AbstractController
{ {
private EntityManagerInterface $entityManager; private EntityManagerInterface $entityManager;
private \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop; private \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop;
public function __construct(EntityManagerInterface $entityManager, \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop) public function __construct(
{ EntityManagerInterface $entityManager,
\PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop,
) {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->hydrateShop = $hydrateShop; $this->hydrateShop = $hydrateShop;
} }
/** #[Response(
* Shops response: 200,
* description: 'shops',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Dto\Shop\Shops\Output::class)),
* response=200, )]
* description="shops", #[Tag(name: 'Shops')]
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Shop\Shops\Output::class)) #[Security(name: 'ApiKeyAuth')]
* ) #[Security(name: 'Bearer')]
* @OA\Tag(name="Shops")
* @Security(name="ApiKeyAuth")
* @Security(name="Bearer")
*/
#[Route(path: '/shops', methods: ['GET'])] #[Route(path: '/shops', methods: ['GET'])]
#[IsGranted('ROLE_SHOP')] #[IsGranted('ROLE_SHOP')]
public function allAction(): JsonResponse public function allAction(): JsonResponse
@ -56,27 +56,25 @@ class Get extends AbstractController
return $this->json(new Shops\Output($data)); return $this->json(new Shops\Output($data));
} }
/** #[Response(
* Get One Shop response: 200,
* description: 'found shop',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Model\Shop::class)),
* response=200, )]
* description="found shop", #[Response(
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Model\Shop::class)) response: 404,
* ) description: 'not found',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Dto\Error\NotFound::class)),
* response=404, )]
* description="not found", #[Tag(name: 'Shops')]
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Error\NotFound::class)) #[Security(name: 'ApiKeyAuth')]
* )
* @OA\Tag(name="Shops")
* @Security(name="ApiKeyAuth")
*/
#[Route(path: '/shops/{id}', methods: ['GET'])] #[Route(path: '/shops/{id}', methods: ['GET'])]
#[IsGranted('ROLE_API')] #[IsGranted('ROLE_API')]
public function one($id): JsonResponse public function one($id): JsonResponse
{ {
$shop = $this->entityManager->getRepository(\PSC\Shop\EntityBundle\Entity\Shop::class)->findOneBy(['uid' => $id]); $shop = $this->entityManager
->getRepository(\PSC\Shop\EntityBundle\Entity\Shop::class)
->findOneBy(['uid' => $id]);
if ($shop) { if ($shop) {
return $this->json($this->hydrateShop->hydrateToModel($shop)); return $this->json($this->hydrateShop->hydrateToModel($shop));

View File

@ -3,9 +3,12 @@
namespace PSC\Component\ApiBundle\Api\Stockbooking; namespace PSC\Component\ApiBundle\Api\Stockbooking;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\RequestBody;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Component\ApiBundle\Dto\Error\PersistFailure; use PSC\Component\ApiBundle\Dto\Error\PersistFailure;
use PSC\Component\ApiBundle\Dto\Shop\Shops; use PSC\Component\ApiBundle\Dto\Shop\Shops;
@ -17,56 +20,58 @@ use PSC\Component\ApiBundle\Model\Shop\Domain;
use PSC\Component\ApiBundle\Model\Stockbooking; use PSC\Component\ApiBundle\Model\Stockbooking;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class CreateByNr extends AbstractController class CreateByNr extends AbstractController
{ {
private EntityManagerInterface $entityManager; private EntityManagerInterface $entityManager;
private \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop; private \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop;
public function __construct(EntityManagerInterface $entityManager, \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop) public function __construct(
{ EntityManagerInterface $entityManager,
\PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop,
) {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->hydrateShop = $hydrateShop; $this->hydrateShop = $hydrateShop;
} }
/** #[Response(
* create by nr response: 200,
* description: 'stockbooking',
* @OA\Response( content: new JsonContent(
* response=200, ref: new Model(type: PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr\Output::class),
* description="stockbooking", ),
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr\Output::class)) )]
* ) #[RequestBody(
* @OA\RequestBody( description: 'This is a request body',
* description="This is a request body", content: new Model(type: PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr\Input::class),
* @Model(type=\PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr\Input::class)) )]
* ) #[Tag(name: 'Stockbooking')]
* @OA\Tag(name="Stockbooking") #[IsGranted('ROLE_API')]
* @IsGranted("ROLE_API") #[Security(name: 'ApiKeyAuth')]
* @Security(name="ApiKeyAuth")
*/
#[Route(path: '/stockbooking/createbynr', methods: ['POST'])] #[Route(path: '/stockbooking/createbynr', methods: ['POST'])]
#[ParamConverter('data', class: '\PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr\Input', converter: 'psc_rest.request_body')] public function create(#[MapRequestPayload] Input $data): JsonResponse
public function create(Input $data): JsonResponse
{ {
$product = $this->entityManager $product = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Product')->createQueryBuilder('product') ->getRepository('PSC\Shop\EntityBundle\Entity\Product')
->createQueryBuilder('product')
->andWhere('product.nrIntern = :nr or product.nrExtern = :nr') ->andWhere('product.nrIntern = :nr or product.nrExtern = :nr')
->setParameter('nr', $data->product_nr) ->setParameter('nr', $data->product_nr)
->getQuery()->getOneOrNullResult(); ->getQuery()
->getOneOrNullResult();
if ($product) { if ($product) {
$contact = $this->entityManager $contact = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Contact')->findOneBy(['uid' => $data->contact_id]); ->getRepository('PSC\Shop\EntityBundle\Entity\Contact')
->findOneBy(['uid' => $data->contact_id]);
if (!$contact) { if (!$contact) {
return $this->json(new NotFound("Contact not found")); return $this->json(new NotFound('Contact not found'));
} }
$stBooking = new \PSC\Shop\EntityBundle\Entity\Stockbooking($product); $stBooking = new \PSC\Shop\EntityBundle\Entity\Stockbooking($product);
@ -90,6 +95,6 @@ class CreateByNr extends AbstractController
return $this->json(new Output()); return $this->json(new Output());
} }
return $this->json(new NotFound("Product not found")); return $this->json(new NotFound('Product not found'));
} }
} }

View File

@ -3,9 +3,12 @@
namespace PSC\Component\ApiBundle\Api\Stockbooking; namespace PSC\Component\ApiBundle\Api\Stockbooking;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\RequestBody;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Dto\Error\NotFound; use PSC\Component\ApiBundle\Dto\Error\NotFound;
use PSC\Component\ApiBundle\Dto\Shop\Shops; use PSC\Component\ApiBundle\Dto\Shop\Shops;
use PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Input; use PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Input;
@ -16,56 +19,55 @@ use PSC\Component\ApiBundle\Model\Shop\Domain;
use PSC\Component\ApiBundle\Model\Stockbooking; use PSC\Component\ApiBundle\Model\Stockbooking;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class GetByNr extends AbstractController class GetByNr extends AbstractController
{ {
private EntityManagerInterface $entityManager; private EntityManagerInterface $entityManager;
private \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop; private \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop;
public function __construct(EntityManagerInterface $entityManager, \PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop) public function __construct(
{ EntityManagerInterface $entityManager,
\PSC\Component\ApiBundle\Hydrate\Shop $hydrateShop,
) {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->hydrateShop = $hydrateShop; $this->hydrateShop = $hydrateShop;
} }
/** #[Response(
* get all by nr response: 200,
* description: 'account',
* @OA\Response( content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Output::class)),
* response=200, )]
* description="account", #[RequestBody(
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Output::class)) description: 'This is a request body',
* ) content: new Model(type: PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Input::class),
* @OA\RequestBody( )]
* description="This is a request body", #[Tag(name: 'Stockbooking')]
* @Model(type=\PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Input::class)) #[IsGranted('ROLE_API')]
* ) #[Security(name: 'ApiKeyAuth')]
* @OA\Tag(name="Stockbooking")
* @IsGranted("ROLE_API")
* @Security(name="ApiKeyAuth")
*/
#[Route(path: '/stockbooking/getallbynr', methods: ['POST'])] #[Route(path: '/stockbooking/getallbynr', methods: ['POST'])]
#[ParamConverter('data', class: '\PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr\Input', converter: 'psc_rest.request_body')] public function allAction(#[MapRequestPayload] Input $data): JsonResponse
public function allAction(Input $data): JsonResponse
{ {
$product = $this->entityManager $product = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Product')->createQueryBuilder('product') ->getRepository('PSC\Shop\EntityBundle\Entity\Product')
->createQueryBuilder('product')
->andWhere('product.nrIntern = :nr or product.nrExtern = :nr') ->andWhere('product.nrIntern = :nr or product.nrExtern = :nr')
->setParameter('nr', $data->nr) ->setParameter('nr', $data->nr)
->getQuery()->getOneOrNullResult(); ->getQuery()
->getOneOrNullResult();
if ($product) { if ($product) {
$bookings = $this->entityManager $bookings = $this->entityManager
->getRepository('PSC\Shop\EntityBundle\Entity\Stockbooking') ->getRepository('PSC\Shop\EntityBundle\Entity\Stockbooking')
->findByProduct($product); ->findByProduct($product);
$bookingsTemp = array(); $bookingsTemp = [];
/** @var \PSC\Shop\EntityBundle\Entity\Stockbooking $booking */ /** @var \PSC\Shop\EntityBundle\Entity\Stockbooking $booking */
foreach ($bookings as $booking) { foreach ($bookings as $booking) {
$contact = new Contact(); $contact = new Contact();
@ -86,6 +88,6 @@ class GetByNr extends AbstractController
return $this->json(new Output($bookingsTemp)); return $this->json(new Output($bookingsTemp));
} }
return $this->json(new NotFound("Product not found")); return $this->json(new NotFound('Product not found'));
} }
} }

View File

@ -3,32 +3,28 @@
namespace PSC\Component\ApiBundle\Api\System; namespace PSC\Component\ApiBundle\Api\System;
use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\DocumentManager;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use PSC\Component\ApiBundle\Model\Instance; use PSC\Component\ApiBundle\Model\Instance;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class Info extends AbstractController class Info extends AbstractController
{ {
/** #[Tag(name: 'System')]
* Get system info. #[Response(
* response: 200,
* description: 'Returns system info',
* @Security(name="ApiKeyAuth") content: new JsonContent(ref: new Model(type: Instance::class)),
* @OA\Response( )]
* response=200,
* description="Returns system info",
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Model\Instance::class))
* )
* @OA\Tag(name="System")
*/
#[Route(path: '/system/info', methods: ['GET'])] #[Route(path: '/system/info', methods: ['GET'])]
#[IsGranted('ROLE_API')] #[IsGranted('ROLE_API')]
public function infoAction(DocumentManager $documentManager): JsonResponse public function infoAction(DocumentManager $documentManager): JsonResponse
@ -38,7 +34,6 @@ class Info extends AbstractController
->getRepository(\PSC\Shop\EntityBundle\Document\Instance::class) ->getRepository(\PSC\Shop\EntityBundle\Document\Instance::class)
->findOneBy(['appId' => '1']); ->findOneBy(['appId' => '1']);
$data = new Instance(); $data = new Instance();
$data->sftpHost = getenv('ftpIp'); $data->sftpHost = getenv('ftpIp');
$data->ip = getenv('ftpIp'); $data->ip = getenv('ftpIp');

View File

@ -2,14 +2,16 @@
namespace PSC\Component\ApiBundle\Api\System; namespace PSC\Component\ApiBundle\Api\System;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use Nelmio\ApiDocBundle\Annotation\Security; use OpenApi\Attributes as OA;
use OpenApi\Annotations as OA; use OpenApi\Attributes\JsonContent;
use OpenApi\Attributes\Response;
use OpenApi\Attributes\Tag;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response as HttpResponse;
use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
class Version extends AbstractController class Version extends AbstractController
@ -21,17 +23,12 @@ class Version extends AbstractController
$this->kernel = $kernel; $this->kernel = $kernel;
} }
/** #[Response(
* Get Release version. response: 200,
* description: 'release version',
* content: new JsonContent(ref: new Model(type: PSC\Component\ApiBundle\Model\Version::class)),
* @OA\Response( )]
* response=200, #[Tag(name: 'System')]
* description="release version",
* @OA\JsonContent(ref=@Model(type=\PSC\Component\ApiBundle\Model\Version::class))
* )
* @OA\Tag(name="System")
*/
#[Route(path: '/system/version', methods: ['GET'])] #[Route(path: '/system/version', methods: ['GET'])]
public function versionAction(): JsonResponse public function versionAction(): JsonResponse
{ {

View File

@ -1,82 +0,0 @@
<?php
namespace PSC\Component\ApiBundle\Converter;
use JMS\Serializer\Exception\UnsupportedFormatException;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\Annotation\Context;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
final class RequestBody implements ParamConverterInterface
{
private $serializer;
private $context = [];
/**
* @param string[]|null $groups
*/
public function __construct(
SerializerInterface $serializer,
?array $groups = null,
?string $version = null,
?string $validationErrorsArgument = null
) {
$this->serializer = $serializer;
if (!empty($groups)) {
$this->context['groups'] = (array) $groups;
}
if (!empty($version)) {
$this->context['version'] = $version;
}
}
/**
* {@inheritdoc}
*/
public function apply(Request $request, ParamConverter $configuration): bool
{
$format = $request->getContentType();
if (null === $format) {
return $this->throwException(new UnsupportedMediaTypeHttpException(), $configuration);
}
try {
$object = $this->serializer->deserialize(
$request->getContent(),
$configuration->getClass(),
$format,
);
} catch (UnsupportedFormatException $e) {
return $this->throwException(new UnsupportedMediaTypeHttpException($e->getMessage(), $e), $configuration);
}
$request->attributes->set($configuration->getName(), $object);
return true;
}
/**
* {@inheritdoc}
*/
public function supports(ParamConverter $configuration): bool
{
return null !== $configuration->getClass() && 'psc_rest.request_body' === $configuration->getConverter();
}
private function throwException(\Exception $exception, ParamConverter $configuration): bool
{
if ($configuration->isOptional()) {
return false;
}
throw $exception;
}
}

View File

@ -2,15 +2,12 @@
namespace PSC\Component\ApiBundle\Dto\Account\GetParentByTitle; namespace PSC\Component\ApiBundle\Dto\Account\GetParentByTitle;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
final class Input final class Input
{ {
/** #[Property(type: 'string')]
* @var string
*
* @OA\Property(type="string")
*/
public string $title; public string $title;
} }

View File

@ -2,22 +2,15 @@
namespace PSC\Component\ApiBundle\Dto\Account\GetParentByTitle; namespace PSC\Component\ApiBundle\Dto\Account\GetParentByTitle;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
final class Output final class Output
{ {
/** #[Property(type: 'string')]
* @var string
*
* @OA\Property(type="string")
*/
public string $parent; public string $parent;
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $parent_id; public int $parent_id;
} }

View File

@ -3,8 +3,10 @@
namespace PSC\Component\ApiBundle\Dto\Cms\Cms; namespace PSC\Component\ApiBundle\Dto\Cms\Cms;
use PSC\Component\ApiBundle\Model\Cms; use PSC\Component\ApiBundle\Model\Cms;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
use OpenApi\Attributes\Items;
class Output class Output
{ {
@ -14,17 +16,9 @@ class Output
$this->count = count($cms); $this->count = count($cms);
} }
/** #[Property(type: 'array', items: new Items(ref: new Model(type: PSC\Component\ApiBundle\Model\Cms::class)))]
* @var Cms[]
*
* @OA\Property(type="array", @OA\Items(ref=@Model(type=\PSC\Component\ApiBundle\Model\Cms::class)))
*/
public array $data = []; public array $data = [];
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $count = 0; public int $count = 0;
} }

View File

@ -3,8 +3,9 @@
namespace PSC\Component\ApiBundle\Dto\Error; namespace PSC\Component\ApiBundle\Dto\Error;
use PSC\Component\ApiBundle\Model\Shop; use PSC\Component\ApiBundle\Model\Shop;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class NotFound class NotFound
{ {
@ -13,8 +14,6 @@ class NotFound
$this->message = $message; $this->message = $message;
} }
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $message = ""; public string $message = "";
} }

View File

@ -3,8 +3,9 @@
namespace PSC\Component\ApiBundle\Dto\Error; namespace PSC\Component\ApiBundle\Dto\Error;
use PSC\Component\ApiBundle\Model\Shop; use PSC\Component\ApiBundle\Model\Shop;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class PersistFailure class PersistFailure
{ {
@ -13,8 +14,6 @@ class PersistFailure
$this->message = $message; $this->message = $message;
} }
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $message = ""; public string $message = "";
} }

View File

@ -2,8 +2,10 @@
namespace PSC\Component\ApiBundle\Dto\Plugin\Plugins; namespace PSC\Component\ApiBundle\Dto\Plugin\Plugins;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
use OpenApi\Attributes\Items;
use PSC\Component\ApiBundle\Model\Plugin; use PSC\Component\ApiBundle\Model\Plugin;
class Output class Output
@ -14,17 +16,9 @@ class Output
$this->count = count($plugins); $this->count = count($plugins);
} }
/** #[Property(type: 'array', items: new Items(ref: new Model(type: PSC\Component\ApiBundle\Model\Plugin::class)))]
* @var Plugin[]
*
* @OA\Property(type="array", @OA\Items(ref=@Model(type=\PSC\Component\ApiBundle\Model\Plugin::class)))
*/
public array $data = []; public array $data = [];
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $count = 0; public int $count = 0;
} }

View File

@ -3,8 +3,10 @@
namespace PSC\Component\ApiBundle\Dto\Shop\Shops; namespace PSC\Component\ApiBundle\Dto\Shop\Shops;
use PSC\Component\ApiBundle\Model\Shop; use PSC\Component\ApiBundle\Model\Shop;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
use OpenApi\Attributes\Items;
class Output class Output
{ {
@ -14,17 +16,9 @@ class Output
$this->count = count($shops); $this->count = count($shops);
} }
/** #[Property(type: 'array', items: new Items(ref: new Model(type: PSC\Component\ApiBundle\Model\Shop::class)))]
* @var Shop[]
*
* @OA\Property(type="array", @OA\Items(ref=@Model(type=\PSC\Component\ApiBundle\Model\Shop::class)))
*/
public array $data = []; public array $data = [];
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $count = 0; public int $count = 0;
} }

View File

@ -2,43 +2,24 @@
namespace PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr; namespace PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Input class Input
{ {
/** #[Property(type: 'string')]
* @var string
*
* @OA\Property(type="string")
*/
public string $product_nr = ""; public string $product_nr = "";
/** #[Property(type: 'string')]
* @var string
*
* @OA\Property(type="string")
*/
public string $description = ""; public string $description = "";
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $amount = 0; public int $amount = 0;
/** #[Property(type: 'string')]
* @var string
*
* @OA\Property(type="string")
*/
public string $delivery_nr = ""; public string $delivery_nr = "";
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $contact_id = 0; public int $contact_id = 0;
} }

View File

@ -2,16 +2,13 @@
namespace PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr; namespace PSC\Component\ApiBundle\Dto\Stockbooking\CreateByNr;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
use PSC\Component\ApiBundle\Model\Stockbooking; use PSC\Component\ApiBundle\Model\Stockbooking;
class Output class Output
{ {
/** #[Property(type: 'boolean')]
* @var bool
*
* @OA\Property(type="boolean")
*/
public bool $success = true; public bool $success = true;
} }

View File

@ -2,15 +2,12 @@
namespace PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr; namespace PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Input class Input
{ {
/** #[Property(type: 'string')]
* @var string
*
* @OA\Property(type="string")
*/
public string $nr = ""; public string $nr = "";
} }

View File

@ -2,8 +2,10 @@
namespace PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr; namespace PSC\Component\ApiBundle\Dto\Stockbooking\GetByNr;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
use OpenApi\Attributes\Items;
use PSC\Component\ApiBundle\Model\Stockbooking; use PSC\Component\ApiBundle\Model\Stockbooking;
class Output class Output
@ -14,17 +16,9 @@ class Output
$this->count = count($shops); $this->count = count($shops);
} }
/** #[Property(type: 'array', items: new Items(ref: new Model(type: PSC\Component\ApiBundle\Model\Stockbooking::class)))]
* @var Stockbooking[]
*
* @OA\Property(type="array", @OA\Items(ref=@Model(type=\PSC\Component\ApiBundle\Model\Stockbooking::class)))
*/
public array $data = []; public array $data = [];
/** #[Property(type: 'integer')]
* @var int
*
* @OA\Property(type="integer")
*/
public int $count = 0; public int $count = 0;
} }

View File

@ -13,25 +13,18 @@
namespace PSC\Component\ApiBundle\Model; namespace PSC\Component\ApiBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Cms class Cms
{ {
/** #[Property(type: 'integer')]
* @OA\Property(type="integer")
*/
public int $id; public int $id;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255) public string $title = '';
*/ #[Property(type: 'string', maxLength: 255)]
public string $title = ""; public string $text = '';
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255) public string $shopId = '';
*/
public string $text = "";
/**
* @OA\Property(type="string", maxLength=255)
*/
public string $shopId = "";
} }

View File

@ -13,34 +13,23 @@
namespace PSC\Component\ApiBundle\Model; namespace PSC\Component\ApiBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Instance class Instance
{ {
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $sftpUserName = ""; public string $sftpUserName = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $sftpPassword; public string $sftpPassword;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $sftpPort; public string $sftpPort;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $sftpHost; public string $sftpHost;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $ip; public string $ip;
/** #[Property(type: 'boolean')]
* @OA\Property(type="boolean")
*/
public bool $smtpOwn = false; public bool $smtpOwn = false;
} }

View File

@ -13,8 +13,9 @@
namespace PSC\Component\ApiBundle\Model; namespace PSC\Component\ApiBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Plugin class Plugin
{ {
@ -24,16 +25,10 @@ class Plugin
$this->title = $title; $this->title = $title;
} }
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $uuid = ""; public string $uuid = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $title = ""; public string $title = "";
/** #[Property(type: 'boolean')]
* @OA\Property(type="boolean")
*/
public bool $installed = false; public bool $installed = false;
} }

View File

@ -13,62 +13,38 @@
namespace PSC\Component\ApiBundle\Model; namespace PSC\Component\ApiBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
use OpenApi\Attributes\Items;
class Shop class Shop
{ {
/** #[Property(type: 'integer')]
* @OA\Property(type="integer")
*/
public int $id = 0; public int $id = 0;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $uuid = ""; public string $uuid = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $name = ""; public string $name = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $basketField1 = ""; public string $basketField1 = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $basketField2 = ""; public string $basketField2 = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $logo1 = ""; public string $logo1 = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $logo2 = ""; public string $logo2 = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $browsericon = ""; public string $browsericon = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $basketPosField1 = ""; public string $basketPosField1 = "";
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $basketPosField2 = ""; public string $basketPosField2 = "";
/** #[Property(type: 'array', items: new Items(ref: new Model(type: PSC\Component\ApiBundle\Model\Shop\Domain::class)))]
* @OA\Property(type="array", @OA\Items(ref=@Model(type=\PSC\Component\ApiBundle\Model\Shop\Domain::class)))
*/
public array $domains = []; public array $domains = [];
/** #[Property(type: 'boolean')]
* @OA\Property(type="boolean")
*/
public bool $disabled = false; public bool $disabled = false;
/** #[Property(type: 'boolean')]
* @OA\Property(type="boolean")
*/
public bool $private = false; public bool $private = false;
/** /**

View File

@ -13,8 +13,9 @@
namespace PSC\Component\ApiBundle\Model\Shop; namespace PSC\Component\ApiBundle\Model\Shop;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Domain class Domain
{ {
@ -25,16 +26,10 @@ class Domain
$this->ssl = $ssl; $this->ssl = $ssl;
} }
/** #[Property(type: 'integer')]
* @OA\Property(type="integer")
*/
public int $id; public int $id;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $name = ""; public string $name = "";
/** #[Property(type: 'boolean')]
* @OA\Property(type="boolean")
*/
public bool $ssl = false; public bool $ssl = false;
} }

View File

@ -13,37 +13,24 @@
namespace PSC\Component\ApiBundle\Model; namespace PSC\Component\ApiBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Stockbooking class Stockbooking
{ {
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $id; public string $id;
/** #[Property(type: 'object')]
* @OA\Property(type="object")
*/
public Contact $contact; public Contact $contact;
/** #[Property(type: 'object')]
* @OA\Property(type="object")
*/
public Product $product; public Product $product;
/** #[Property(type: 'integer')]
* @OA\Property(type="integer")
*/
public int $amount = 0; public int $amount = 0;
/** #[Property(type: 'date')]
* @OA\Property(type="date")
*/
public \DateTime $created; public \DateTime $created;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $deliveryNumber; public string $deliveryNumber;
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $description; public string $description;
} }

View File

@ -13,17 +13,15 @@
namespace PSC\Component\ApiBundle\Model; namespace PSC\Component\ApiBundle\Model;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Annotations as OA; use OpenApi\Attributes as OA;
use OpenApi\Attributes\Property;
class Version class Version
{ {
/** #[Property(type: 'string', maxLength: 255)]
* @OA\Property(type="string", maxLength=255)
*/
public string $datum = ""; public string $datum = "";
/**
* @OA\Property(type="string", maxLength=255) #[Property(type: 'string', maxLength: 255)]
*/
public string $release = ""; public string $release = "";
} }

View File

@ -1,4 +1,4 @@
psc_component_api_system: psc_component_api_system:
resource: "@PSCComponentApiBundle/Api" resource: "@PSCComponentApiBundle/Api"
prefix: /api prefix: /api
type: annotation type: attribute

View File

@ -5,7 +5,3 @@ services:
PSC\Component\ApiBundle\: PSC\Component\ApiBundle\:
resource: '../../*/*' resource: '../../*/*'
PSC\Component\ApiBundle\Converter\RequestBody:
tags:
- { name: request.param_converter, converter: "psc_rest.request_body" }

View File

@ -26,8 +26,8 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException;
/** /**

View File

@ -14,9 +14,9 @@
namespace PSC\Component\SteplayouterBundle\Calendar\Type; namespace PSC\Component\SteplayouterBundle\Calendar\Type;
use PSC\Component\SteplayouterBundle\Calendar\Type; use PSC\Component\SteplayouterBundle\Calendar\Type;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
/** /**
* DefaultController fürs ProductionBundle * DefaultController fürs ProductionBundle

View File

@ -14,9 +14,9 @@
namespace PSC\Component\SteplayouterBundle\Calendar\Type; namespace PSC\Component\SteplayouterBundle\Calendar\Type;
use PSC\Component\SteplayouterBundle\Calendar\Type; use PSC\Component\SteplayouterBundle\Calendar\Type;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
class Hochvariable extends Type class Hochvariable extends Type
{ {

View File

@ -14,9 +14,9 @@
namespace PSC\Component\SteplayouterBundle\Calendar\Type; namespace PSC\Component\SteplayouterBundle\Calendar\Type;
use PSC\Component\SteplayouterBundle\Calendar\Type; use PSC\Component\SteplayouterBundle\Calendar\Type;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
/** /**
* DefaultController fürs ProductionBundle * DefaultController fürs ProductionBundle

View File

@ -21,28 +21,14 @@ class PrintPdfCommand extends Command
{ {
protected function configure() protected function configure()
{ {
$this $this->setName('component:steplayouter:pdf:print')
->setName('component:steplayouter:pdf:print')
->setDescription('Create Print PDF') ->setDescription('Create Print PDF')
->addArgument( ->addArgument('outputDir', InputArgument::REQUIRED, 'Layouter Id')
'outputDir', ->addArgument('layouterUid', InputArgument::REQUIRED, 'Layouter Id')
InputArgument::REQUIRED, ->addArgument('aliasUid', InputArgument::REQUIRED, 'Alias Id');
'Layouter Id'
)
->addArgument(
'layouterUid',
InputArgument::REQUIRED,
'Layouter Id'
)
->addArgument(
'aliasUid',
InputArgument::REQUIRED,
'Alias Id'
)
;
} }
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output): int
{ {
$layouter_uuid = $input->getArgument('layouterUid'); $layouter_uuid = $input->getArgument('layouterUid');
$outfilename = $input->getArgument('outputDir'); $outfilename = $input->getArgument('outputDir');
@ -52,5 +38,7 @@ class PrintPdfCommand extends Command
$pdfService = $this->getContainer()->get('psc.component.steplayouter.pdf'); $pdfService = $this->getContainer()->get('psc.component.steplayouter.pdf');
$pdfService->create($outfilename, $layouter_uuid, $aliasUid); $pdfService->create($outfilename, $layouter_uuid, $aliasUid);
return 0;
} }
} }

View File

@ -18,11 +18,11 @@ use PSC\Component\SteplayouterBundle\Calendar\Type\Quer;
use PSC\Component\SteplayouterBundle\Calendar\Type\Hoch; use PSC\Component\SteplayouterBundle\Calendar\Type\Hoch;
use PSC\Component\SteplayouterBundle\Calendar\Design; use PSC\Component\SteplayouterBundle\Calendar\Design;
use PSC\Component\SteplayouterBundle\Calendar\Generator; use PSC\Component\SteplayouterBundle\Calendar\Generator;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
/** /**
* CalendarController fürs SteplayouterBundle * CalendarController fürs SteplayouterBundle

View File

@ -13,9 +13,9 @@
namespace PSC\Component\SteplayouterBundle\Controller; namespace PSC\Component\SteplayouterBundle\Controller;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/** /**
@ -33,7 +33,7 @@ class DefaultController extends AbstractController
* @return View * @return View
*/ */
#[Route(path: '/component/steplayouter', name: 'component_steplayouter')] #[Route(path: '/component/steplayouter', name: 'component_steplayouter')]
#[Template] #[Template('@PSCComponentSteplayouter/default/index.html.twig')]
public function indexAction() public function indexAction()
{ {
return array('stylePath' => '{{stylePath}}'); return array('stylePath' => '{{stylePath}}');

File diff suppressed because one or more lines are too long

View File

@ -23,8 +23,8 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException;
use PSC\Component\SteplayouterBundle\Config\Converter\ToArray; use PSC\Component\SteplayouterBundle\Config\Converter\ToArray;

View File

@ -25,8 +25,8 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException;
/** /**

View File

@ -24,9 +24,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
/** /**
@ -45,8 +45,8 @@ class CheckController extends AbstractController
* @return View * @return View
*/ */
#[Route(path: '/check/index', name: 'psc_component_steplayouter_system_check_index')] #[Route(path: '/check/index', name: 'psc_component_steplayouter_system_check_index')]
#[Template] #[Template('@PSCComponentSteplayouter/system/check/index.html.twig')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function indexAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, EntityManagerInterface $entityManager) public function indexAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, EntityManagerInterface $entityManager)
{ {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */

View File

@ -24,9 +24,9 @@ use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Attribute\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Bridge\Twig\Attribute\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
/** /**
@ -49,8 +49,8 @@ class SettingsController extends AbstractController
* @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\ORMException
*/ */
#[Route(path: '/settings/index', name: 'psc_component_steplayouter_system_settings_index')] #[Route(path: '/settings/index', name: 'psc_component_steplayouter_system_settings_index')]
#[Template] #[Template('@PSCComponentSteplayouter/system/settings/index.html.twig')]
#[Security("is_granted('ROLE_SHOP')")] #[IsGranted('ROLE_SHOP')]
public function indexAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, DocumentManager $documentManager) public function indexAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, DocumentManager $documentManager)
{ {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */

Some files were not shown because too many files have changed in this diff Show More