This commit is contained in:
Thomas Peterson 2024-12-06 17:56:37 +01:00
parent 1a49dc9097
commit d74cadada6
148 changed files with 10163 additions and 8546 deletions

View File

@ -6,6 +6,9 @@ services:
build:
context: ../../
dockerfile: ./.docker/images/php/base/Dockerfile
platforms:
- "linux/amd64"
- "linux/arm64"
args:
- ALPINE_VERSION=${ALPINE_VERSION?}
- APP_CODE_PATH=${APP_CODE_PATH_CONTAINER?}

View File

@ -5,6 +5,9 @@ services:
image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/application-${ENV?}:${TAG?}
build:
context: ../
platforms:
- "linux/amd64"
- "linux/arm64"
dockerfile: ./images/php/application/Dockerfile
target: ${ENV?}
args:

View File

@ -5,6 +5,9 @@ services:
image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/php-fpm-${ENV?}:${TAG?}
build:
context: ../
platforms:
- "linux/amd64"
- "linux/arm64"
dockerfile: ./images/php/fpm/Dockerfile
target: ${ENV?}
args:
@ -14,6 +17,9 @@ services:
image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/php-cron-${ENV?}:${TAG?}
build:
context: ../
platforms:
- "linux/amd64"
- "linux/arm64"
dockerfile: ./images/php/cron/Dockerfile
target: ${ENV?}
args:
@ -24,6 +30,9 @@ services:
image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/web-${ENV?}:${TAG?}
build:
context: ../../
platforms:
- "linux/amd64"
- "linux/arm64"
dockerfile: ./.docker/images/nginx/Dockerfile
target: ${ENV?}
args:

View File

@ -63,6 +63,9 @@ services:
image: ${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/application-${ENV?}:${TAG?}
build:
context: ../
platforms:
- "linux/amd64"
- "linux/arm64"
dockerfile: ./images/php/application/Dockerfile
args:
- BASE_IMAGE=${DOCKER_REGISTRY?}/${DOCKER_NAMESPACE?}/php-base-${ENV?}:${TAG?}

View File

@ -41,7 +41,7 @@ server {
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Content-Security-Policy "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'" always;
add_header Content-Security-Policy "default-src https: 'unsafe-eval' 'unsafe-inline'; object-src 'none'; worker-src blob:; img-src https: blob: data:" always;
fastcgi_temp_path /tmp/fastcgi 1 2;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm:9000;

View File

@ -5,5 +5,5 @@ opcache.revalidate_freq = "0"
assert.exception = 1
zend.assertions = 1
display_error=0
display_errors=0
error_reporting=E_ALL & ~E_NOTICE & ~E_DEPRECATED

File diff suppressed because one or more lines are too long

View File

@ -72,6 +72,7 @@
"symfony/framework-bundle": "*",
"symfony/http-client": "*",
"symfony/intl": "*",
"symfony/lock": "6.4.*",
"symfony/mailer": "*",
"symfony/mime": "*",
"symfony/monolog-bundle": "^3.8",

1403
src/new/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,5 +5,10 @@ declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('doctrine_mongodb', ['auto_generate_proxy_classes' => true, 'auto_generate_hydrator_classes' => true, 'connections' => ['default' => ['server' => '%env(resolve:MONGODB_URL)%', 'options' => []]], 'default_database' => '%env(resolve:MONGODB_DB)%', 'document_managers' => ['default' => ['auto_mapping' => true]]]);
$containerConfigurator->extension(
'doctrine_mongodb',
['auto_generate_proxy_classes' => true,
'auto_generate_hydrator_classes' => true,
'connections' => ['default' => ['server' => '%env(resolve:MONGODB_URL)%', 'options' => []]], 'default_database' => '%env(resolve:MONGODB_DB)%', 'document_managers' => ['default' => ['auto_mapping' => true]]]
);
};

View File

@ -0,0 +1,2 @@
framework:
lock: '%env(LOCK_DSN)%'

View File

@ -2,17 +2,23 @@
declare(strict_types=1);
use PSC\Shop\EntityBundle\Document\Instance;
use PSC\Shop\EntityBundle\Entity\Contact;
use PSC\Shop\EntityBundle\Entity\Shop;
use PSC\Shop\UserBundle\Model\ApiUser;
use PSC\Shop\UserBundle\Security\ApiKeyAuthenticator;
use PSC\Shop\UserBundle\Security\ApiKeyExtractor;
use PSC\Shop\UserBundle\Security\ApiKeyHandler;
use PSC\Shop\UserBundle\Security\ApiKey\ApiKeyProvider;
use PSC\Shop\UserBundle\Security\ApiKey\InstanceProvider;
use PSC\Shop\UserBundle\Security\ApiKey\ShopProvider;
use PSC\Shop\UserBundle\Security\User\UserProvider;
use PSC\Shop\UserBundle\Security\ZendAuthenticator;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('security',
$containerConfigurator->extension(
'security',
[
'password_hashers' =>
[
@ -61,16 +67,30 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'ROLE_PRODUCTION',
'ROLE_SUPER_SHOP',
'ROLE_ALLOWED_TO_SWITCH'
],
'ROLE_API' => [
'ROLE_SHOP'
]
],
'providers' => [
'database' => ['entity' => ['class' => Contact::class]],
'database_token' => ['entity' => ['class' => Shop::class]],
//'database' => ['entity' => ['class' => Contact::class]],
'user_provider' => ['id' => UserProvider::class ],
'shop_provider' => ['id' => ShopProvider::class ],
'instance_provider' => ['id' => InstanceProvider::class ],
'all' => [
'chain' =>
[
'providers' =>
['user_provider', 'shop_provider', 'instance_provider']
]
]
// 'database_token' => ['entity' => ['class' => Shop::class]],
// 'database_api_key' => ['entity' => ['class' => Instance::class]],
],
'firewalls' => [
'admin_secured_area' => [
'pattern' => '^/backend',
'provider' => 'database',
'provider' => 'user_provider',
'form_login' => [
'check_path' => 'psc_backend_login',
'login_path' => 'psc_backend_login',
@ -87,7 +107,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'api_login' => [
'pattern' => '/api/login',
'stateless' => false,
'provider' => 'database',
'provider' => 'all',
'json_login' => [
'check_path' => '/api/login_check',
'success_handler' => 'lexik_jwt_authentication.handler.authentication_success',
@ -97,7 +117,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'api' => [
'pattern' => '^/api',
'stateless' => false,
'provider' => 'database',
'provider' => 'all',
'jwt' => null,
'access_token' => [
'token_handler' => ApiKeyHandler::class,
@ -106,7 +126,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
],
'storefront' => [
'pattern' => '^/',
'provider' => 'database',
'provider' => 'user_provider',
'stateless' => false,
'jwt' => null,
'custom_authenticators' => [
@ -141,5 +161,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'roles' => 'PUBLIC_ACCESS'
]
]
]);
]
);
};

View File

@ -41,6 +41,36 @@ PSC\Shop\EntityBundle\Entity\Contact:
street: "Chausseestraße3"
house_number: "24b3"
contact_4:
username: company@shop.de
email: company@shop.de
name: company@shop.de
password: test2014
enable: true
setRolesForm: ['@role_1','@role_5']
firstname: "Thomas"
company: "PSC"
lastname: "Peterson"
city: "Gribow"
zip: "17506"
street: "Chausseestraße"
house_number: "24b"
contact_5:
username: association@shop.de
email: association@shop.de
name: association@shop.de
password: test2014
enable: true
setRolesForm: ['@role_1','@role_5']
firstname: "Thomas"
company: "ASSOCATION"
lastname: "Peterson"
city: "Gribow"
zip: "17506"
street: "Chausseestraße"
house_number: "24b"
PSC\Shop\EntityBundle\Entity\ContactAddress:
contactaddress_1:

View File

@ -1,4 +1,4 @@
PSC\Shop\EntityBundle\Entity\Install:
install_1:
uid: 1
# uid: 1
color_db: "TEST"

View File

@ -51,3 +51,40 @@ PSC\Shop\EntityBundle\Entity\Product:
price: 20
set_config: '{}'
shop: '@shop_1'
product_5:
uid: 9
title: XML Calc Test 9 Example
language: 'de'
url: xml_test_9
uuid: 01938686-0e4d-7da9-bae3-b2e1b1681f9f
type: 6
pos: <numberBetween(1, 200)>
private: false
taxClass: 19
set_config: '{}'
shop: '@shop_1'
calcXml: >
<?xml version="1.0" encoding="utf-8"?>
<kalkulation>
<artikel>
<name>SD-Durchschreibesätze A4-Blocks</name>
<kommentar>210 mm x 297 mm</kommentar>
<option id="auflage" name="Auflage" type="Input" default="10"/>
<option id="calc_rabatt" type="Hidden">
<contact.accountType>
<grenze calc_value="1">0-1</grenze>
<grenze calc_value="0.8">2</grenze>
<grenze calc_value="0.5">3</grenze>
</contact.accountType>
</option>
<option id="calc" type="Hidden">
<auflage>
<grenze formel="0.26*$Vauflage$V*$CVcalc_rabatt_contact.accountType$CV">1-</grenze>
</auflage>
</option>
</artikel>
</kalkulation>

View File

@ -82,7 +82,7 @@ class DashboardController extends AbstractController
}
$chart = $chartBuilder->createChart(Chart::TYPE_LINE);
$chart->setData([
'labels' => ['Januar', 'Februar', 'März', 'April', 'May', 'Juli', 'Juni', 'August', 'September', 'Oktober', 'November', 'Dezember'],
'labels' => ['Januar', 'Februar', 'März', 'April', 'May', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
'datasets' => [
[
'label' => $year1,

View File

@ -27,7 +27,7 @@ class OrderposExporter implements ExporterInterface, ConfigurableElementInterfac
private $_shopService = null;
/** @var Form */
private $_form = null;
function __construct(FormFactoryInterface $formFactory, EntityManagerInterface $entityManager, Shop $shopService)
public function __construct(FormFactoryInterface $formFactory, EntityManagerInterface $entityManager, Shop $shopService)
{
$this->_formFactory = $formFactory;
$this->_entityManager = $entityManager;
@ -88,7 +88,7 @@ class OrderposExporter implements ExporterInterface, ConfigurableElementInterfac
$row[0]->getOrder()->getAlias(),
$row[0]->getOrder()->getCreated()->format('Y-m-d H:i:s'),
$row[0]->getOrder()->getStatus(),
$row[0]->getOrder()->getBrutto(),
round($row[0]->getPriceAllBrutto(), 2),
$row[0]->getOrder()->getPackage(),
$row[0]->getOrder()->getContact()->getUsername(),
$row[0]->getProduct()->getUid(),

View File

@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
class Login extends AbstractController
{
/**
@ -46,7 +47,8 @@ class Login extends AbstractController
}
$output = new Output();
$output->userName = $user->getContact()->getUsername();
$output->email = $user->getContact()->getEmail();
$output->token = $jwtManager->create($user->getContact());
return $this->json($output);

View File

@ -174,7 +174,7 @@ class EditController extends AbstractController
$contactDoc->setLayouterEmail($contact->layouterEmail);
$contactDoc->setLayouterCountryCode($contact->layouterCountryCode);
$contactDoc->setLayouterCountryName($contact->layouterCountryName);
$contactDoc->setAccountType($contact->accountType);
$documentManager->persist($contactDoc);
$documentManager->flush();
@ -309,7 +309,7 @@ class EditController extends AbstractController
$contact->layouterCountryName = $contactDoc->getLayouterCountryName();
$contact->layouterCountryCode = $contactDoc->getLayouterCountryCode();
$contact->layouterEmail = $contactDoc->getLayouterEmail();
$contact->accountType = $contactDoc->getAccountType();
$contact->setProductsOrg($contact->getProducts());
$contact->setProductsSub($contact->getProducts());
} elseif (!$contactDoc) {
@ -325,6 +325,7 @@ class EditController extends AbstractController
$contactDoc->setLayouterEmail($contact->layouterEmail);
$contactDoc->setLayouterCountryCode($contact->layouterCountryCode);
$contactDoc->setLayouterCountryName($contact->layouterCountryName);
$contactDoc->setAccountType($contact->accountType);
$contact->setProductsOrg($contact->getProducts());
$contact->setProductsSub($contact->getProducts());
@ -390,7 +391,7 @@ class EditController extends AbstractController
$contactDoc->setLayouterEmail($contact->layouterEmail);
$contactDoc->setLayouterCountryCode($contact->layouterCountryCode);
$contactDoc->setLayouterCountryName($contact->layouterCountryName);
$contactDoc->setAccountType($contact->accountType);
$this->historyService->createHistoryEntry(new PSCHistory((string)$contact->getUid()), $contact, $contactDoc);
$entityManager->persist($contact);
$entityManager->flush();

View File

@ -13,4 +13,19 @@ class Output
* @OA\Property(type="string")
*/
public string $token;
/**
* @var string
*
* @OA\Property(type="string")
*/
public string $userName;
/**
* @var string
*
* @OA\Property(type="string")
*/
public string $email;
}

View File

@ -15,6 +15,7 @@ namespace PSC\Shop\ContactBundle\Form\Backend\General;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PSC\Shop\ContactBundle\Model\AccountType;
use PSC\Shop\EntityBundle\Entity\Payment;
use PSC\Shop\EntityBundle\Entity\Productgroup;
use PSC\Shop\EntityBundle\Entity\Shipping;
@ -28,6 +29,7 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
@ -224,6 +226,15 @@ class ContactType extends AbstractType
'choice_value' => 'uid',
'multiple' => true
))
->add('accountType', EnumType::class, [
'required' => true,
'class' => AccountType::class,
'label' => 'accountType',
'label_attr' => [
'data-bs-toggle' => "tooltip",
'data-bs-html' => '{{ contact.accountType }}'
]
])
->add('bankKtoName', TextType::class, [
'required' => false,
'label' => 'accountowner',

View File

@ -0,0 +1,10 @@
<?php
namespace PSC\Shop\ContactBundle\Model;
enum AccountType: int
{
case PERSONAL = 1;
case COMPANY = 2;
case ASSOCIATION = 3;
}

View File

@ -90,6 +90,8 @@ class Contact
#[OA\Property(type: 'string')]
private string $custom24 = "";
private AccountType $accountType;
#[OA\Property(type: 'string')]
private string $countryCode = "";
@ -100,6 +102,17 @@ class Contact
#[OA\Property(type: 'array', items: new OA\Items(ref: new Model(type: Role::class)))]
private array $roles = [];
public function getAccountType(): AccountType
{
return $this->accountType;
}
public function setAccountType(AccountType $var): void
{
$this->accountType = $var;
}
public function getShops(): array
{
return $this->shops;
@ -118,6 +131,7 @@ class Contact
{
$this->layouterData = new LayouterData();
$this->account = new Account();
$this->accountType = AccountType::PERSONAL;
}
public function getUuid(): string

View File

@ -1,4 +1,5 @@
customer: Kunde
accountType: Kontotyp
detail: bearbeiten
Company: Firma
Companyaddition: Firma Zusatz

View File

@ -312,6 +312,20 @@
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="header"><h5>{{ form_label(form.accountType) }}</h5></div>
<div class="body">
<div class="row">
<div class="col-md-12">
{{ form_widget(form.accountType) }}
</div>
{{ form_errors(form.accountType) }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -296,6 +296,19 @@
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-default">
<div class="header"><h5>{{ form_label(form.accountType) }}</h5></div>
<div class="body">
<div class="row">
<div class="col-md-12">
{{ form_widget(form.accountType) }}
</div>
{{ form_errors(form.accountType) }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -149,6 +149,8 @@ class Contact
$contact->setEnable((bool)$contactEntity->isEnabled());
$contact->setCollectingOrders((bool)$contactEntity->getCollectingOrders());
$contact->setAccountType($contactDoc->getAccountType());
$contact->setCustom1((string)$contactDoc->getCustom1());
$contact->setCustom2((string)$contactDoc->getCustom2());
$contact->setCustom3((string)$contactDoc->getCustom3());

View File

@ -9,11 +9,11 @@ use PSC\Shop\EntityBundle\Entity\Order;
class Contact
{
public function __construct(private readonly EntityManagerInterface $entityManager,
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly \PSC\Shop\ContactBundle\Transformer\Model\Contact $modelTransformer,
private readonly DocumentManager $documentManager)
{
private readonly DocumentManager $documentManager
) {
}
public function toDb(\PSC\Shop\ContactBundle\Model\Contact $contact, Order $orderEntity)
@ -28,6 +28,5 @@ class Contact
public function fromDb(\PSC\Shop\ContactBundle\Model\Contact $contact, PSCContact $contactEntity)
{
$this->modelTransformer->fromDb($contact, $contactEntity);
}
}

View File

@ -16,6 +16,7 @@ namespace PSC\Shop\EntityBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Field;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Id;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Document;
use PSC\Shop\ContactBundle\Model\AccountType;
#[Document]
class Contact
@ -212,9 +213,20 @@ class Contact
*/
#[Field(type: 'float')]
protected $priceFactor;
/**
* @return bool
*/
#[Field(type: 'int')]
protected $accountType = 1;
public function getAccountType(): AccountType
{
return AccountType::tryFrom($this->accountType);
}
public function setAccountType(AccountType $type): void
{
$this->accountType = $type->value;
}
public function isShowOtherOrders()
{
return $this->showOtherOrders;

View File

@ -0,0 +1,37 @@
<?php
namespace PSC\Shop\EntityBundle\Document\Embed;
use Doctrine\ODM\MongoDB\Mapping\Annotations\EmbeddedDocument;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Field;
use PSC\Shop\ContactBundle\Model\AccountType;
#[EmbeddedDocument]
class Contact
{
#[Field(type: 'string')]
private $uuid;
#[Field(type: 'int')]
private $accountType;
public function getUuid(): string
{
return (string)$this->uuid;
}
public function setUuid(string $var): void
{
$this->uuid = $var;
}
public function getAccountType(): AccountType
{
return AccountType::tryFrom($this->accountType);
}
public function setAccountType(AccountType $var): void
{
$this->accountType = $var->value;
}
}

View File

@ -1,16 +1,5 @@
<?php
/**
* PrintshopCreator Suite
*
* PHP Version 5.3
*
* @author Thomas Peterson <info@thomas-peterson.de>
* @copyright 2012-2013 PrintshopCreator GmbH
* @license Private
* @link http://www.printshopcreator.de
*/
namespace PSC\Shop\EntityBundle\Document\Embed;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Field;

View File

@ -16,9 +16,10 @@ namespace PSC\Shop\EntityBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Field;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Id;
use Doctrine\ODM\MongoDB\Mapping\Annotations\Document;
use Symfony\Component\Security\Core\User\UserInterface;
#[Document]
class Instance
class Instance implements UserInterface
{
#[Id]
protected $id;
@ -631,6 +632,17 @@ class Instance
{
$this->parcelCancelationNumberStart = $parcelCancelationNumberStart;
}
public function getRoles(): array
{
return ['ROLE_API'];
}
public function eraseCredentials()
{
}
public function getUserIdentifier(): string
{
return $this->id;
}

View File

@ -88,6 +88,9 @@ class Order
protected $deliveryAddressSaved;
#[EmbedOne]
protected $senderAddressSaved;
#[EmbedOne]
protected $contactSaved;
/**
* @var boolean $withTax
*/

View File

@ -54,6 +54,10 @@ class Payment
*/
#[Field(type: 'hash')]
protected $pluginSettings = [];
#[Field(type: 'string')]
protected $paymentTerms;
public function __get($name)
{
// TODO: Implement __get() method.
@ -64,6 +68,15 @@ class Payment
// TODO: Implement __set() method.
}
public function getPaymentTerms(): string
{
return (string)$this->paymentTerms;
}
public function setPaymentTerms(string $var): void
{
$this->paymentTerms = $var;
}
/**
* @return string
*/

View File

@ -16,6 +16,7 @@ namespace PSC\Shop\EntityBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use PSC\Shop\ContactBundle\Model\AccountType;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
@ -63,7 +64,7 @@ class Contact implements UserInterface, PasswordAuthenticatedUserInterface, \Ser
public $layouterEmail;
public $layouterCountryCode;
public $layouterCountryName;
public $accountType = AccountType::PERSONAL;
public $showOtherOrders = false;
public $showOtherOrdersAccountFilter;
public $showOtherOrdersAccount;

View File

@ -31,7 +31,7 @@ class Payment extends Paymenttype
protected $shippings = [];
/** @var array */
protected $countrys = [];
/** @var string */
protected $paymentTerms;
protected $paymentGateway;
/** @var array */
protected $paymentGatewaySettings = [];
@ -401,6 +401,16 @@ class Payment extends Paymenttype
$this->paymentGateway = $paymentGateway;
}
public function getPaymentTerms(): string
{
return $this->paymentTerms;
}
public function setPaymentTerms(string $paymentTerms): void
{
$this->paymentTerms = $paymentTerms;
}
/**
* @return array
*/

View File

@ -57,9 +57,6 @@ abstract class Base
*/
#[\OpenApi\Attributes\Property(ref: new Model(type: \PSC\Shop\ShippingBundle\Model\Shipping::class))]
private Shipping $shipping;
/**
* @var Payment
*/
#[\OpenApi\Attributes\Property(ref: new Model(type: \PSC\Shop\PaymentBundle\Model\Payment::class))]
private $payment;
@ -395,7 +392,7 @@ abstract class Base
*/
public function getGross()
{
return $this->gross;
return $this->net + $this->vat;
}
/**
@ -695,6 +692,16 @@ abstract class Base
return $sum;
}
public function getPositionsNet(): int
{
$sum = 0;
foreach ($this->positions as $position) {
$sum += $position->getPrice()->getAllNet();
}
return $sum;
}
public function getShop(): Shop
{
return $this->shop;

View File

@ -0,0 +1,37 @@
<?php
namespace PSC\Shop\OrderBundle\Model\Order;
use PSC\Shop\ContactBundle\Model\AccountType;
class Contact
{
private string $uuid;
private AccountType $accountType;
public function __construct()
{
$this->accountType = AccountType::PERSONAL;
}
public function getUuid(): string
{
return $this->uuid;
}
public function setUuid(string $uuid): void
{
$this->uuid = $uuid;
}
public function getAccountType(): AccountType
{
return $this->accountType;
}
public function setAccountType(AccountType $var): void
{
$this->accountType = $var;
}
}

View File

@ -6,6 +6,7 @@ use Brick\Money\Money;
use PSC\Component\ApiBundle\Transformer\Shop;
use PSC\Shop\PaymentBundle\Service\Price;
use PSC\Shop\PaymentBundle\Transformer\Payment;
use PSC\Shop\ProductBundle\Interfaces\ICalcNeedContact;
use PSC\Shop\ShippingBundle\Transformer\Shipping;
use PSC\Shop\VoucherBundle\Service\Calc as CalcVoucher;
use PSC\Shop\VoucherBundle\Transformer\Voucher as PSCVoucher;
@ -13,8 +14,8 @@ use PSC\System\PluginBundle\Service\ProductType;
class Calc
{
public function __construct(private readonly Price $pricePayment,
public function __construct(
private readonly Price $pricePayment,
private readonly Shop $shopTransformer,
private readonly \PSC\Shop\ShippingBundle\Service\Price $priceShipping,
private readonly ProductType $productTypeRegistry,
@ -58,6 +59,9 @@ class Calc
if ($this->productTypeRegistry->getProductType($position->getProduct()->getSpecialProductTypeObject()->getTyp())) {
$specialProductTransformer = $this->productTypeRegistry->getProductType($position->getProduct()->getSpecialProductTypeObject()->getTyp())->getProducer();
if ($specialProductTransformer) {
if ($specialProductTransformer instanceof ICalcNeedContact) {
$specialProductTransformer->setContact($order->getContact());
}
$specialProductTransformer->setProduct($position->getProduct());
$specialProductTransformer->calcPriceForOrderPosition($position);
}

View File

@ -7,6 +7,7 @@ use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\EntityManagerInterface;
use PSC\Shop\ContactBundle\Model\Contact;
use PSC\Shop\ContactBundle\Service\Address;
use PSC\Shop\EntityBundle\Entity\Order as PSCOrder;
use PSC\Shop\EntityBundle\Entity\Orderpos;
use PSC\Shop\OrderBundle\Model\Base as PSCBase;
use PSC\Shop\OrderBundle\Transformer\Order\Position;
@ -286,7 +287,7 @@ class Order
* @param $uuid
* @return \PSC\Shop\OrderBundle\Model\Order
*/
public function getOrderByUuid($uuid, $load_only_order = false)
public function getOrderByUuid($uuid, $load_only_order = false): PSCBase
{
$this->load_only_order = $load_only_order;
$orderRepo = $this->entityManager->getRepository('PSC\Shop\EntityBundle\Entity\Order');

View File

@ -78,10 +78,6 @@ class Order extends Base
$this->paymentTransformer->fromDb($order->getPayment(), $orderEntity->getPaymentType());
$this->shippingTransformer->fromDb($order->getShipping(), $orderEntity->getShippingType());
/**
* DOC
*/
$invoice = new \PSC\Shop\ContactBundle\Model\Address();
if ($orderDoc->getInvoiceAddressSaved() == null) {
$this->addressTransformer->fromDb($invoice, new ContactAddress());
@ -167,10 +163,6 @@ class Order extends Base
$this->contactTransformer->toDb($order->getContact(), $orderEntity);
$this->accountTransformer->toDb($order->getAccount(), $orderEntity);
/**
* DOC
*/
$invoiceDoc = new ContactAddress();
$this->addressTransformer->toDb($order->getInvoiceAddress(), $invoiceDoc);
$orderDoc->setInvoiceAddressSaved($invoiceDoc);

View File

@ -90,6 +90,7 @@ class PaymentType extends AbstractType
->add('priceFrom', IntegerType::class, array('label' => 'priceof', 'required' => true, 'empty_data' => 0))
->add('priceTo', IntegerType::class, array('label' => 'priceuntil', 'required' => true, 'empty_data' => 0))
->add('description', TextareaType::class, array('label' => 'description', 'required' => false))
->add('paymentTerms', TextareaType::class, array('label' => 'paymentTerms', 'required' => false))
->add('trustedShopName', TextType::class, array('label' => 'TrustedShopName', 'required' => false))
->add('paymentGateway', ChoiceType::class, array(
'label' => 'PaymentGateway',

View File

@ -1,5 +1,7 @@
Uid: Uid
active: Aktiv
paymentTerms: Zahlungsbedingung
paymentTermsExample: 'Beispiel: Zahlbar in 14 Tagen bis zum {{ order.created|date_modify("+14 day")|date("d.m.Y") }}'
Pos: Pos
Name: Name
cost: Kosten

View File

@ -125,6 +125,19 @@
<label class="col-md-1 form-control-label">{{ form_label(form.description) }}</label>
<div class="col-md-11">
{{ form_widget(form.description) }}
<p class="text-muted">
{{ "paymentTermsExample"|trans}}
</p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="row mb-3">
<label class="col-md-1 form-control-label">{{ form_label(form.paymentTerms) }}</label>
<div class="col-md-11">
{{ form_widget(form.paymentTerms) }}
</div>
</div>
</div>

View File

@ -129,6 +129,19 @@
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="row mb-3">
<label class="col-md-1 form-control-label">{{ form_label(form.paymentTerms) }}</label>
<div class="col-md-11">
{{ form_widget(form.paymentTerms) }}
<p class="text-muted">
{{ "paymentTermsExample"|trans}}
</p>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="filter" role="tabpanel">
<div class="row">

View File

@ -0,0 +1,10 @@
<?php
namespace PSC\Shop\ProductBundle\Interfaces;
use PSC\Shop\ContactBundle\Model\Contact;
interface ICalcNeedContact
{
public function setContact(Contact $contact): void;
}

View File

@ -25,6 +25,8 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\FlockStore;
#[AsCommand(name: 'application:queue:do')]
class DoCommand extends Command
@ -80,6 +82,14 @@ class DoCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output)
{
$store = new FlockStore();
$factory = new LockFactory($store);
$lock = $factory->createLock('doQueue');
if ($lock->acquire()) {
foreach ($this->internalQueues as $queue) {
$queue->execute();
}
@ -87,7 +97,8 @@ class DoCommand extends Command
$queues = $this->mongoDb
->getRepository('PSC\Shop\EntityBundle\Document\Queue')
->findBy(
array('eventType' => 'system_plugin_every_run', 'active' => true), [
array('eventType' => 'system_plugin_every_run', 'active' => true),
[
'pos' => 'ASC'
]
);
@ -156,7 +167,8 @@ class DoCommand extends Command
'shop' => $job->getShop(),
'active' => true,
'eventType' => $job->getEvent()
), [
),
[
'pos' => 'ASC'
]
);
@ -229,9 +241,11 @@ class DoCommand extends Command
if (count($this->eventManager->getAll()) == 0) {
$this->mongoDb->createQueryBuilder(JobLog::class)->remove()->getQuery()->execute();
}
}
date_default_timezone_set('Europe/Berlin');
$output->writeln("Successfully ".date('Y-m-d H:i:s'));
return 0;
}
}

View File

@ -93,6 +93,8 @@ class ShopSettingsType extends AbstractType
->add('senderFax', TextType::class, array('required' => false, 'label' => 'Fax'))
->add('senderMobile', TextType::class, array('required' => false, 'label' => 'mobilephonenumber'))
->add('senderEmail', TextType::class, array('required' => false, 'label' => 'EMail'))
->add('senderIban', TextType::class, array('required' => false, 'label' => 'Iban'))
->add('senderLId', TextType::class, array('required' => false, 'label' => 'Leitweg Id'))
->add('priceFactor', NumberType::class, array('required' => false, 'label' => 'Factor', 'scale' => 5, 'html5' => true,
'attr' => array(

View File

@ -0,0 +1,48 @@
<?php
namespace PSC\Shop\UserBundle\Security\ApiKey;
use Doctrine\ODM\MongoDB\DocumentManager;
use PSC\Shop\EntityBundle\Document\Instance;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class InstanceProvider implements UserProviderInterface
{
public function __construct(private DocumentManager $repository)
{
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof Instance) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
$refreshedUser = $this->repository->getRepository(Instance::class)->find($user->getId());
return $refreshedUser;
}
public function supportsClass($class)
{
return Instance::class == $class;
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
$instance = $this->repository
->getRepository(\PSC\Shop\EntityBundle\Document\Instance::class)
->findOneBy(['supporttoken' => $identifier]);
if (null === $instance) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException(sprintf('Instance "%s" not found.', $identifier));
}
return $instance;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace PSC\Shop\UserBundle\Security\ApiKey;
use PSC\Shop\EntityBundle\Entity\Shop;
use PSC\Shop\EntityBundle\Repository\ShopRepository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class ShopProvider implements UserProviderInterface
{
public function __construct(private ShopRepository $repository)
{
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof Shop) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
$refreshedUser = $this->repository->find($user->getId());
return $refreshedUser;
}
/**
* {@inheritDoc}
*/
public function supportsClass($class)
{
return Shop::class == $class;
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
$user = $this->repository->getShopByApiKey($identifier);
if (null === $user) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException(sprintf('Shop "%s" not found.', $identifier));
}
return $user;
}
}

View File

@ -2,6 +2,7 @@
namespace PSC\Shop\UserBundle\Security;
use Doctrine\ODM\MongoDB\DocumentManager;
use PSC\Shop\EntityBundle\Repository\ShopRepository;
use PSC\Shop\UserBundle\Model\ApiUser;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
@ -11,7 +12,8 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
class ApiKeyHandler implements AccessTokenHandlerInterface
{
public function __construct(
private ShopRepository $repository
private ShopRepository $repository,
private readonly DocumentManager $documentManager
) {
}
@ -20,7 +22,17 @@ class ApiKeyHandler implements AccessTokenHandlerInterface
// e.g. query the "access token" database to search for this token
$accessToken = $this->repository->findOneBy(['apiKey' => $apiToken]);
if (null === $accessToken) {
$instance = $this->documentManager
->getRepository(\PSC\Shop\EntityBundle\Document\Instance::class)
->findOneBy(['supporttoken' => $apiToken]);
if (!$instance) {
throw new BadCredentialsException('Invalid credentials.');
} else {
return new UserBadge($instance->getId(), function () use ($instance) {
return $instance;
});
}
}
// and return a UserBadge object containing the user identifier from the found token

View File

@ -1,119 +0,0 @@
<?php
namespace PSC\Shop\UserBundle\Security;
use Doctrine\ORM\EntityManagerInterface;
use PSC\Shop\UserBundle\Security\Authentication\Token\pscAppToken;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\AuthenticatorInterface;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class Authenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
use TargetPathTrait;
private const LOGIN_ROUTE = 'psc_backend_login';
private const LOGIN_SUCCESS_ROUTE = 'psc_backend_dashboard_index';
private $encoder;
private $entityManager;
/**
* @var UrlGeneratorInterface
*/
private $urlGenerator;
/**
* @var CsrfTokenManagerInterface
*/
private $csrfTokenManager;
/**
* @var UserPasswordEncoderInterface
*/
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $encoder, EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager)
{
$this->encoder = $encoder;
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
public function supports(Request $request)
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'username' => $request->request->get('username'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $userProvider->loadUserByUsername($credentials['username']);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('User could not be found.');
}
$_SESSION['Zend_Auth']['storage']['self_email'] = $user->getUsername();
$_SESSION['Zend_Auth']['storage']['id'] = $user->getId();
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return password_verify($credentials['password'], $user->getPassword());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse(self::LOGIN_SUCCESS_ROUTE);
}
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
}

View File

@ -1,137 +0,0 @@
<?php
namespace PSC\Shop\UserBundle\Security;
use Doctrine\ORM\EntityManagerInterface;
use PSC\Shop\EntityBundle\Entity\Contact;
use PSC\Shop\UserBundle\PSCShopUserBundle;
use PSC\System\SettingsBundle\Document\LogEntry;
use PSC\System\SettingsBundle\Service\Log;
use PSC\System\SettingsBundle\Service\Shop;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'psc_backend_login';
private const LOGIN_SUCCESS_ROUTE = 'psc_backend_dashboard_index';
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
private $shopService;
/**
* @var Log
*/
private Log $logService;
public function __construct(
EntityManagerInterface $entityManager,
UrlGeneratorInterface $urlGenerator,
CsrfTokenManagerInterface $csrfTokenManager,
UserPasswordEncoderInterface $passwordEncoder,
Shop $shopService,
Log $logService
) {
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
$this->shopService = $shopService;
$this->logService = $logService;
}
public function supports(Request $request)
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'username' => $request->request->get('username'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$shop = $this->shopService->getShopByDomain();
$user = $this->entityManager
->getRepository(Contact::class)
->getContactByEmailAndShop($credentials['username'], $shop->getUid());
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
$user = $user->getContact();
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
$auth = password_verify($credentials['password'], $user->getPassword());
if ($auth) {
$this->logService->createLogEntry($this->shopService->getShopByDomain(), new Contact(), LogEntry::INFO, PSCShopUserBundle::class, $credentials['username'], 'Login Successful');
} else {
$this->logService->createLogEntry($this->shopService->getShopByDomain(), new Contact(), LogEntry::INFO, PSCShopUserBundle::class, $credentials['username'], 'Login Failure');
}
return $auth;
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate(self::LOGIN_SUCCESS_ROUTE));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}

View File

@ -1,146 +0,0 @@
<?php
/**
* PrintshopCreator Suite
*
* PHP Version 5.3
*
* @author Thomas Peterson <info@thomas-peterson.de>
* @copyright 2012-2013 PrintshopCreator GmbH
* @license Private
* @link http://www.printshopcreator.de
*/
namespace PSC\Shop\UserBundle\Security\User;
use Doctrine\Bundle\DoctrineBundle\Registry;
use PSC\Shop\UserBundle\Model\ApiUser;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class Provider implements UserProviderInterface
{
private $class;
private $repository;
private $property;
private $metadata;
public function __construct(Registry $registry, $class, $property = null, $managerName = null)
{
$em = $registry->getManager($managerName);
$this->class = $class;
$this->metadata = $em->getClassMetadata($class);
if (false !== strpos($this->class, ':')) {
$this->class = $this->metadata->getName();
}
$this->repository = $em->getRepository($class);
$this->property = $property;
}
/**
* {@inheritdoc}
*/
public function loadUserByUsername($username)
{
if (null !== $this->property) {
$user = $this->repository->findOneBy(array($this->property => $username));
} else {
if (!$this->repository instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($this->repository)));
}
$user = $this->repository->loadUserByUsername($username);
}
if (null === $user) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
return $user;
}
/**
* {@inheritDoc}
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof $this->class) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
if ($this->repository instanceof UserProviderInterface) {
$refreshedUser = $this->repository->refreshUser($user);
} else {
// The user must be reloaded via the primary key as all other data
// might have changed without proper persistence in the database.
// That's the case when the user has been changed by a form with
// validation errors.
if (!$id = $this->metadata->getIdentifierValues($user)) {
throw new \InvalidArgumentException("You cannot refresh a user " .
"from the EntityUserProvider that does not contain an identifier. " .
"The user object has to be serialized with its own identifier " .
"mapped by Doctrine.");
}
if (null === $refreshedUser = $this->repository->find($id)) {
throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($id)));
}
}
return $refreshedUser;
}
/**
* {@inheritDoc}
*/
public function supportsClass($class)
{
return $class === $this->class || is_subclass_of($class, $this->class);
}
public function loadUserByUId($id)
{
if (null !== $this->property) {
$user = $this->repository->findOneBy(array('uid' => $id));
} else {
if (!$this->repository instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($this->repository)));
}
$user = $this->repository->loadUserByUid($id);
}
if (null === $user) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException(sprintf('User "%s" not found.', $id));
}
return $user;
}
public function mySelectedShop($contact)
{
return $this->repository->mySelectedShop($contact);
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
if (null !== $this->property) {
$user = $this->repository->findOneBy(array('username' => $identifier));
} else {
if (!$this->repository instanceof UserProviderInterface) {
throw new \InvalidArgumentException(sprintf('The Doctrine repository "%s" must implement UserProviderInterface.', get_class($this->repository)));
}
$user = $this->repository->loadUserByUid($identifier);
}
if (null === $user) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException(sprintf('User "%s" not found.', $identifier));
}
return $user;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace PSC\Shop\UserBundle\Security\User;
use PSC\Shop\ContactBundle\Repository\ContactRepository;
use PSC\Shop\EntityBundle\Entity\Contact;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class UserProvider implements UserProviderInterface
{
public function __construct(private ContactRepository $repository)
{
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof Contact) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
$refreshedUser = $this->repository->find($user->getId());
return $refreshedUser;
}
/**
* {@inheritDoc}
*/
public function supportsClass($class)
{
return Contact::class == $class || is_subclass_of($class, Contact::class);
}
public function mySelectedShop($contact)
{
return $this->repository->mySelectedShop($contact);
}
public function loadUserByIdentifier(string $identifier): UserInterface
{
$user = $this->repository->loadUserByUsername($identifier);
if (null === $user) {
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException(sprintf('User "%s" not found.', $identifier));
}
return $user;
}
}

View File

@ -25,7 +25,7 @@ class HelpController extends AbstractController
$helpItems = $documentManager
->getRepository(Help::class)
->findAll();
->findBy([], ['name' => 'ASC']);
return array(
'helpItems' => $helpItems
);

View File

@ -13,13 +13,17 @@ class Help
$this->mongoManager = $mongoManager;
}
public function getHelp(string $name): ?\PSC\System\SettingsBundle\Model\Help
public function getHelp(string $id, string $name): ?\PSC\System\SettingsBundle\Model\Help
{
/** @var \PSC\System\SettingsBundle\Document\Help $doc */
$doc = $this->mongoManager->getRepository(\PSC\System\SettingsBundle\Document\Help::class)->findOneBy(['name' => $name]);
$doc = $this->mongoManager->getRepository(\PSC\System\SettingsBundle\Document\Help::class)->findOneBy(['name' => sprintf("%s_%s", $id, $name)]);
if (null === $doc) {
$doc = $this->mongoManager->getRepository(\PSC\System\SettingsBundle\Document\Help::class)->findOneBy(['name' => $name]);
if ($doc === null) {
return null;
}
}
return new \PSC\System\SettingsBundle\Model\Help($doc->id, $doc->name, $doc->helpText);
}
}

View File

@ -16,6 +16,7 @@ use Twig\Environment;
use horstoeko\zugferd\ZugferdDocumentBuilder;
use horstoeko\zugferd\ZugferdDocumentPdfMerger;
use horstoeko\zugferd\ZugferdProfiles;
use horstoeko\zugferd\codelists\ZugferdPaymentMeans;
require_once __DIR__ . '/../../../Shop/EntityBundle/Lagacy/TP_Basket_Item.php';
/**
@ -67,35 +68,47 @@ class Printing
public function generateXRechnung(\PSC\Shop\EntityBundle\Entity\Order $order): string
{
$orderModel = $this->orderService->getOrderByUuid($order->getUuid());
$document = ZugferdDocumentBuilder::CreateNew(ZugferdProfiles::PROFILE_XRECHNUNG_3);
$document = ZugferdDocumentBuilder::createNew(ZugferdProfiles::PROFILE_XRECHNUNG_3);
$document
->setDocumentInformation($orderModel->getAlias(), "380", $orderModel->getCreated(), "EUR")
->addDocumentNote('Rechnung gemäß Bestellung vom ' . $orderModel->getCreated()->format('d.m.Y'))
->setDocumentSupplyChainEvent($orderModel->getCreated())
->setDocumentSeller($orderModel->getSenderAddress()->getCompany() . ' ' . $orderModel->getSenderAddress()->getFirstname() . ' ' . $orderModel->getSenderAddress()->getLastname(), "549910")
->setDocumentSellerAddress($orderModel->getSenderAddress()->getStreet() . ' ' . $orderModel->getSenderAddress()->getHouseNumber(), "", "", $orderModel->getSenderAddress()->getZip(), $orderModel->getSenderAddress()->getCity(), $orderModel->getSenderAddress()->getCountry())
->setDocumentBuyerReference("leitwegId")
// ->addDocumentSellerGlobalId("4000001123452", "0088")
->setDocumentSeller($orderModel->getSenderAddress()->getCompany() . ' ' . $orderModel->getSenderAddress()->getFirstname() . ' ' . $orderModel->getSenderAddress()->getLastname(), "")
->setDocumentSellerAddress($orderModel->getSenderAddress()->getStreet() . ' ' . $orderModel->getSenderAddress()->getHouseNumber(), "", "", $orderModel->getSenderAddress()->getZip(), $orderModel->getSenderAddress()->getCity(), $orderModel->getSenderAddress()->getCountry() ?? 'DE')
->setDocumentSellerContact($orderModel->getSenderAddress()->getFirstname() . ' ' . $orderModel->getSenderAddress()->getLastname(), $orderModel->getSenderAddress()->getCompany(), $orderModel->getSenderAddress()->getPhone(), $orderModel->getSenderAddress()->getMobilPhone(), $orderModel->getSenderAddress()->getEmail())
->setDocumentSellerCommunication("EM", $orderModel->getSenderAddress()->getEmail())
->setDocumentBuyer($orderModel->getInvoiceAddress()->getCompany() . ' ' . $orderModel->getInvoiceAddress()->getFirstname() . ' ' . $orderModel->getInvoiceAddress()->getLastname(), $orderModel->getInvoiceAddress()->getKundenNr())
->setDocumentBuyerAddress($orderModel->getInvoiceAddress()->getStreet() . ' ' . $orderModel->getInvoiceAddress()->getHouseNumber(), "", "", $orderModel->getInvoiceAddress()->getZip(), $orderModel->getInvoiceAddress()->getCity(), $orderModel->getInvoiceAddress()->getCountry());
->setDocumentBuyerAddress($orderModel->getInvoiceAddress()->getStreet() . ' ' . $orderModel->getInvoiceAddress()->getHouseNumber(), "", "", $orderModel->getInvoiceAddress()->getZip(), $orderModel->getInvoiceAddress()->getCity(), $orderModel->getInvoiceAddress()->getCountry() ?? 'DE')
->addDocumentPaymentMean(ZugferdPaymentMeans::UNTDID_4461_58, null, null, null, null, null, "DE12500105170648489890", null, null, null)
->addDocumentPaymentTermXRechnung("Zahlungsbedingungen", [], [], [], null, null)
->addDocumentSellerVATRegistrationNumber("DEumsatzsteuer")
->addDocumentSellerTaxNumber("steuernummer")
;
foreach ($orderModel->getTaxes() as $tax) {
$document->addDocumentTax("S", "VAT", $tax->getValue() / 100, $tax->getValue() / 100, $tax->getName() / 100);
}
$document
->setDocumentSummation($orderModel->getGross() / 100, $orderModel->getGross() / 100, $orderModel->getNet() / 100, 0.0, 0.0, $orderModel->getNet() / 100, $orderModel->getTaxesSum() / 100, null, 0.0);
->setDocumentSummation($orderModel->getGross() / 100, $orderModel->getGross() / 100, $orderModel->getPositionsNet() / 100, 20, 0.0, $orderModel->getNet() / 100, $orderModel->getTaxesSum() / 100, null, 0.0);
foreach ($orderModel->getPositions() as $position) {
$document
->addNewPosition($position->getPos())
->setDocumentPositionProductDetails($position->getProduct()->getTitle())
->setDocumentPositionGrossPrice($position->getPrice()->getGross() / 100)
// ->setDocumentPositionGrossPrice($position->getPrice()->getGross() / 100)
->setDocumentPositionNetPrice($position->getPrice()->getNet() / 100)
->setDocumentPositionQuantity(1, "H87")
->addDocumentPositionTax('S', 'VAT', $position->getPrice()->getTax()->getName() / 100)
->setDocumentPositionLineSummation($position->getPrice()->getNet() / 100)
;
}
$document
->addDocumentLogisticsServiceCharge("Shippingcost", 20, ['VAT'], [19]);
return $document->getContent();
}
@ -226,16 +239,16 @@ class Printing
if ($addXRechnung) {
$orderModel = $this->orderService->getOrderByUuid($order->getUuid());
$document = ZugferdDocumentBuilder::CreateNew(ZugferdProfiles::PROFILE_XRECHNUNG_2);
$document = ZugferdDocumentBuilder::CreateNew(ZugferdProfiles::PROFILE_XRECHNUNG_3);
$document
->setDocumentInformation($orderModel->getAlias(), "380", $orderModel->getCreated(), "EUR")
->addDocumentNote('Rechnung gemäß Bestellung vom ' . $orderModel->getCreated()->format('d.m.Y'))
->setDocumentSupplyChainEvent($orderModel->getCreated())
->setDocumentSeller($orderModel->getSenderAddress()->getCompany() . ' ' . $orderModel->getSenderAddress()->getFirstname() . ' ' . $orderModel->getSenderAddress()->getLastname(), "549910")
->setDocumentSellerAddress($orderModel->getSenderAddress()->getStreet() . ' ' . $orderModel->getSenderAddress()->getHouseNumber(), "", "", $orderModel->getSenderAddress()->getZip(), $orderModel->getSenderAddress()->getCity(), $orderModel->getSenderAddress()->getCountry())
->setDocumentSellerAddress($orderModel->getSenderAddress()->getStreet() . ' ' . $orderModel->getSenderAddress()->getHouseNumber(), "", "", $orderModel->getSenderAddress()->getZip(), $orderModel->getSenderAddress()->getCity(), $orderModel->getSenderAddress()->getCountry() ?? 'DE')
->setDocumentBuyer($orderModel->getInvoiceAddress()->getCompany() . ' ' . $orderModel->getInvoiceAddress()->getFirstname() . ' ' . $orderModel->getInvoiceAddress()->getLastname(), $orderModel->getInvoiceAddress()->getKundenNr())
->setDocumentBuyerAddress($orderModel->getInvoiceAddress()->getStreet() . ' ' . $orderModel->getInvoiceAddress()->getHouseNumber(), "", "", $orderModel->getInvoiceAddress()->getZip(), $orderModel->getInvoiceAddress()->getCity(), $orderModel->getInvoiceAddress()->getCountry());
->setDocumentBuyerAddress($orderModel->getInvoiceAddress()->getStreet() . ' ' . $orderModel->getInvoiceAddress()->getHouseNumber(), "", "", $orderModel->getInvoiceAddress()->getZip(), $orderModel->getInvoiceAddress()->getCity(), $orderModel->getInvoiceAddress()->getCountry() ?? 'DE');
foreach ($orderModel->getTaxes() as $tax) {
$document->addDocumentTax("S", "VAT", $tax->getValue() / 100, $tax->getValue() / 100, $tax->getName() / 100);

View File

@ -451,7 +451,7 @@ class TemplateVars
'steuer' => $steuer,
'brutto' => $brutto,
);
} elseif ($product->hasCalcXml()) {
} elseif ($product->hasCalcXml() && $product->getType() == 6) {
$paperContainer = new PaperContainer();
$paperContainer->parse(simplexml_load_string($shop->getInstall()->getPaperContainer()));

View File

@ -59,7 +59,7 @@ if(isset($_options["logo"]) && $_options["logo"] == true) {
}
}
$options->outputBase64 = false;
$options->scale = 30;
//$options->scale = 30;
if(isset($_options["logo"]) && $_options["logo"] !== "") {
$options->addLogoSpace = true;
$options->logoSpaceWidth = 13;

View File

@ -0,0 +1,12 @@
<?php
namespace PSC\System\UpdateBundle\Migrations;
class Version202411271406 extends Base
{
public function migrateDatabase(): void
{
$this->entityManager->getConnection()->exec("ALTER TABLE orderspos ADD INDEX created_date (createdd);");
$this->entityManager->getConnection()->exec("ALTER TABLE papierdb ADD INDEX art_nr (art_nr);");
}
}

View File

@ -671,6 +671,18 @@
"symfony/intl": {
"version": "v4.4.7"
},
"symfony/lock": {
"version": "6.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.2",
"ref": "8e937ff2b4735d110af1770f242c1107fdab4c8e"
},
"files": [
"config/packages/lock.yaml"
]
},
"symfony/mailer": {
"version": "4.3",
"recipe": {

View File

@ -91,7 +91,7 @@
<script>
$.post("{{ instanceService.getInstance().formatedSupportUrl }}/apilogin?X-AUTH-TOKEN={{ instanceService.getInstance().supporttoken }}&to=bridge", function(msg){
if(msg != "") {
$(".swarm").html(msg);
//$(".swarm").html(msg);
}
});
</script>

View File

@ -0,0 +1,128 @@
<?php
namespace App\Tests\PSC\Shop\Order\Api;
use PSC\Shop\EntityBundle\Repository\JobRepository;
use Tests\RefreshDatabaseTrait;
use PSC\Shop\ContactBundle\Repository\ContactRepository;
use PSC\Shop\EntityBundle\Entity\Shop;
use PSC\Shop\EntityBundle\Repository\ShopRepository;
use PSC\Shop\PaymentBundle\Repository\PaymentRepository;
use PSC\Shop\ShippingBundle\Repository\ShippingRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class UpdateTest extends WebTestCase
{
use RefreshDatabaseTrait;
public function testCreateOrderDefault(): void
{
$client = static::createClient();
$shopRepository = static::getContainer()->get(ShopRepository::class);
/**
* @var Shop $shop
*/
$shop = $shopRepository->findOneBy(['title' => 'Printchampion']);
$shippingRepository = static::getContainer()->get(ShippingRepository::class);
$paymentRepository = static::getContainer()->get(PaymentRepository::class);
$client->jsonRequest(
'POST',
'/api/order/create',
[
'shop' => [
'uuid' => (string)$shop->getUuid()
],
'type' => 2,
'shipping' => [
'uid' => $shippingRepository->findOneBy(['title' => 'Deutschlandweit'])->getUid()
],
'payment' => [
'uid' => $paymentRepository->findOneBy(['title' => 'per Rechnung'])->getUid()
],
'draft' => true,
'deliveryAddress' => [
'firstname' => 'Thomas',
'lastname' => 'Peterson',
'street' => 'Chausseestr.',
'houseNumber' => '24',
'zip' => '17506',
'city' => 'Gribow'
],
'invoiceAddress' => [
'firstname' => 'Thomas',
'lastname' => 'Peterson',
'street' => 'Chausseestr.',
'houseNumber' => '24',
'zip' => '17400',
'city' => 'Berlin'
],
'positions' => [
[
'count' => 1,
'product' => [
'title' => 'test XML',
'specialProductTypeObject' => [
'typ' => 6,
'taxClass' => 1900,
'xml' => '<?xml version="1.0" encoding="utf-8"?>
<kalkulation>
<artikel>
<name>Blocks A5 25blatt geleimt</name>
<kommentar>kein</kommentar>
<option id="auflage" name="Auflage" type="Input" width="3" require="true" default="1">
<auflage>
<grenze formel="(10*5)">1-</grenze>
</auflage>
</option>
</artikel>
</kalkulation>'
]
]
],
[
'count' => 1,
'product' => [
'title' => 'test Manual Position',
'specialProductTypeObject' => [
'typ' => 1,
'cent' => true,
'net' => 145,
'taxClass' => 1900
]
]
]
]
],
['HTTP_apiKey' => $shop->getApiKey()]
);
self::assertSame(200, $client->getResponse()->getStatusCode());
$data = json_decode($client->getResponse()->getContent(), true);
$createdDate = $data['created'];
$client->jsonRequest(
'POST',
'/api/order/getonebyuuid',
[
'uuid' => $data['uuid'],
],
['HTTP_apiKey' => $shop->getApiKey()]
);
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame($createdDate, $data['created']);
self::assertSame(200, $client->getResponse()->getStatusCode());
}
}

View File

@ -0,0 +1,329 @@
<?php
namespace App\Tests\PSC\Shop\Order\Service;
use PSC\Shop\ContactBundle\Model\AccountType;
use PSC\Shop\ContactBundle\Model\Contact;
use Tests\RefreshDatabaseTrait;
use PSC\Component\ApiBundle\Model\Shop;
use PSC\Shop\ContactBundle\Model\Address;
use PSC\Shop\OrderBundle\Model\Order\Position;
use PSC\Shop\OrderBundle\Service\Calc;
use PSC\Shop\PaymentBundle\Model\Payment;
use PSC\Shop\ProductBundle\Model\Product;
use PSC\Shop\ProductBundle\Model\ProductSpecialObject;
use PSC\Shop\ShippingBundle\Model\Shipping;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class CalcContactAccountTypeTest extends KernelTestCase
{
use RefreshDatabaseTrait;
public function testWithContactCalc()
{
self::bootKernel();
$container = static::getContainer();
/** @var Calc $calcService */
$calcService = $container->get(Calc::class);
$shop = new Shop();
$shop->setUuid('shop1');
$order = new \PSC\Shop\OrderBundle\Model\Order();
$order->setShop($shop);
$contact = new Contact();
$order->setContact($contact);
$shipping = new Shipping();
$shipping->setTaxClass(7);
$shipping->setPrice(560);
$payment = new Payment();
$payment->setTaxClass(19);
$payment->setPrice(450);
$order->setShipping($shipping);
$order->setPayment($payment);
$deliveryAddress = new Address();
$deliveryAddress->setCountry('de');
$deliveryAddress->setZip(17506);
$order->setDeliveryAddress($deliveryAddress);
$productW0 = new Product();
$productW0->setTitle('Manuelle Position 1');
$specialProductSettingsW0 = new ProductSpecialObject();
$specialProductSettingsW0->setCent(true);
$specialProductSettingsW0->setNet(13.12 * 100);
$productW0->setSpecialProductTypeObject($specialProductSettingsW0);
$positionW0 = new Position();
$positionW0->setProduct($productW0);
$order->addPosition($positionW0);
$productW1 = new Product();
$productW1->setTitle('Manuelle Position 2');
$specialProductSettingsW1 = new ProductSpecialObject();
$specialProductSettingsW1->setNet(34);
$productW1->setSpecialProductTypeObject($specialProductSettingsW1);
$positionW1 = new Position();
$positionW1->setProduct($productW1);
$order->addPosition($positionW1);
$productW2 = new Product();
$productW2->setTitle('Manuelle Position XML Calc W1');
$specialProductSettingsW2 = new \Plugin\System\PSC\XmlCalc\Model\ProductSpecialObject();
$specialProductSettingsW2->setXml(file_get_contents(__DIR__ . '/calc2.xml'));
$specialProductSettingsW2->setParams(['auflage' => 10]);
$productW2->setSpecialProductTypeObject($specialProductSettingsW2);
$positionW2 = new Position();
$positionW2->setProduct($productW2);
$order->addPosition($positionW2);
$productW3 = new Product();
$productW3->setTitle('Manuelle Position XML Calc W2');
$specialProductSettingsW3 = new \Plugin\System\PSC\XmlCalc\Model\ProductSpecialObject();
$specialProductSettingsW3->setXml(file_get_contents(__DIR__ . '/calc1.xml'));
$specialProductSettingsW3->setParams(['auflage' => 1000, 'auswahl_1' => 2]);
$productW3->setSpecialProductTypeObject($specialProductSettingsW3);
$positionW3 = new Position();
$positionW3->setProduct($productW3);
$order->addPosition($positionW3);
$calcService->calcOrder($order);
$this->assertSame(1312, $positionW0->getPrice()->getNet());
$this->assertSame(3400, $positionW1->getPrice()->getNet());
$this->assertSame(210, $positionW2->getPrice()->getNet());
$this->assertSame(15000, $positionW3->getPrice()->getNet());
$this->assertSame(2850, $positionW3->getPrice()->getVat());
$this->assertSame(20932, $order->getNet());
$this->assertSame(3912, $order->getVat());
$this->assertSame(24844, $order->getGross());
}
public function testWithoutContactCalc()
{
self::bootKernel();
$container = static::getContainer();
/** @var Calc $calcService */
$calcService = $container->get(Calc::class);
$shop = new Shop();
$shop->setUuid('shop1');
$order = new \PSC\Shop\OrderBundle\Model\Order();
$order->setShop($shop);
$shipping = new Shipping();
$shipping->setTaxClass(7);
$shipping->setPrice(560);
$payment = new Payment();
$payment->setTaxClass(19);
$payment->setPrice(450);
$order->setShipping($shipping);
$order->setPayment($payment);
$deliveryAddress = new Address();
$deliveryAddress->setCountry('de');
$deliveryAddress->setZip(17506);
$order->setDeliveryAddress($deliveryAddress);
$productW0 = new Product();
$productW0->setTitle('Manuelle Position 1');
$specialProductSettingsW0 = new ProductSpecialObject();
$specialProductSettingsW0->setCent(true);
$specialProductSettingsW0->setNet(13.12 * 100);
$productW0->setSpecialProductTypeObject($specialProductSettingsW0);
$positionW0 = new Position();
$positionW0->setProduct($productW0);
$order->addPosition($positionW0);
$productW1 = new Product();
$productW1->setTitle('Manuelle Position 2');
$specialProductSettingsW1 = new ProductSpecialObject();
$specialProductSettingsW1->setNet(34);
$productW1->setSpecialProductTypeObject($specialProductSettingsW1);
$positionW1 = new Position();
$positionW1->setProduct($productW1);
$order->addPosition($positionW1);
$productW2 = new Product();
$productW2->setTitle('Manuelle Position XML Calc W1');
$specialProductSettingsW2 = new \Plugin\System\PSC\XmlCalc\Model\ProductSpecialObject();
$specialProductSettingsW2->setXml(file_get_contents(__DIR__ . '/calc2.xml'));
$specialProductSettingsW2->setParams(['auflage' => 10]);
$productW2->setSpecialProductTypeObject($specialProductSettingsW2);
$positionW2 = new Position();
$positionW2->setProduct($productW2);
$order->addPosition($positionW2);
$productW3 = new Product();
$productW3->setTitle('Manuelle Position XML Calc W2');
$specialProductSettingsW3 = new \Plugin\System\PSC\XmlCalc\Model\ProductSpecialObject();
$specialProductSettingsW3->setXml(file_get_contents(__DIR__ . '/calc1.xml'));
$specialProductSettingsW3->setParams(['auflage' => 1000, 'auswahl_1' => 2]);
$productW3->setSpecialProductTypeObject($specialProductSettingsW3);
$positionW3 = new Position();
$positionW3->setProduct($productW3);
$order->addPosition($positionW3);
$calcService->calcOrder($order);
$this->assertSame(1312, $positionW0->getPrice()->getNet());
$this->assertSame(3400, $positionW1->getPrice()->getNet());
$this->assertSame(210, $positionW2->getPrice()->getNet());
$this->assertSame(15000, $positionW3->getPrice()->getNet());
$this->assertSame(2850, $positionW3->getPrice()->getVat());
$this->assertSame(20932, $order->getNet());
$this->assertSame(3912, $order->getVat());
$this->assertSame(24844, $order->getGross());
}
public function testWithContactCompanyCalc()
{
self::bootKernel();
$container = static::getContainer();
/** @var Calc $calcService */
$calcService = $container->get(Calc::class);
$shop = new Shop();
$shop->setUuid('shop1');
$order = new \PSC\Shop\OrderBundle\Model\Order();
$order->setShop($shop);
$contact = new Contact();
$contact->setAccountType(AccountType::COMPANY);
$order->setContact($contact);
$shipping = new Shipping();
$shipping->setTaxClass(7);
$shipping->setPrice(560);
$payment = new Payment();
$payment->setTaxClass(19);
$payment->setPrice(450);
$order->setShipping($shipping);
$order->setPayment($payment);
$deliveryAddress = new Address();
$deliveryAddress->setCountry('de');
$deliveryAddress->setZip(17506);
$order->setDeliveryAddress($deliveryAddress);
$productW0 = new Product();
$productW0->setTitle('Manuelle Position 1');
$specialProductSettingsW0 = new ProductSpecialObject();
$specialProductSettingsW0->setCent(true);
$specialProductSettingsW0->setNet(13.12 * 100);
$productW0->setSpecialProductTypeObject($specialProductSettingsW0);
$positionW0 = new Position();
$positionW0->setProduct($productW0);
$order->addPosition($positionW0);
$productW1 = new Product();
$productW1->setTitle('Manuelle Position 2');
$specialProductSettingsW1 = new ProductSpecialObject();
$specialProductSettingsW1->setNet(34);
$productW1->setSpecialProductTypeObject($specialProductSettingsW1);
$positionW1 = new Position();
$positionW1->setProduct($productW1);
$order->addPosition($positionW1);
$productW2 = new Product();
$productW2->setTitle('Manuelle Position XML Calc W1');
$specialProductSettingsW2 = new \Plugin\System\PSC\XmlCalc\Model\ProductSpecialObject();
$specialProductSettingsW2->setXml(file_get_contents(__DIR__ . '/calc2.xml'));
$specialProductSettingsW2->setParams(['auflage' => 10]);
$productW2->setSpecialProductTypeObject($specialProductSettingsW2);
$positionW2 = new Position();
$positionW2->setProduct($productW2);
$order->addPosition($positionW2);
$productW3 = new Product();
$productW3->setTitle('Manuelle Position XML Calc W2');
$specialProductSettingsW3 = new \Plugin\System\PSC\XmlCalc\Model\ProductSpecialObject();
$specialProductSettingsW3->setXml(file_get_contents(__DIR__ . '/calc1.xml'));
$specialProductSettingsW3->setParams(['auflage' => 1000, 'auswahl_1' => 2]);
$productW3->setSpecialProductTypeObject($specialProductSettingsW3);
$positionW3 = new Position();
$positionW3->setProduct($productW3);
$order->addPosition($positionW3);
$calcService->calcOrder($order);
$this->assertSame(1312, $positionW0->getPrice()->getNet());
$this->assertSame(3400, $positionW1->getPrice()->getNet());
$this->assertSame(168, $positionW2->getPrice()->getNet());
$this->assertSame(15000, $positionW3->getPrice()->getNet());
$this->assertSame(2850, $positionW3->getPrice()->getVat());
$this->assertSame(20890, $order->getNet());
$this->assertSame(3904, $order->getVat());
$this->assertSame(24794, $order->getGross());
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<kalkulation>
<artikel>
<name>SD-Durchschreibesätze A4-Blocks</name>
<kommentar>210 mm x 297 mm</kommentar>
<option id="auflage" name="Auflage" type="Input" default="10"/>
<option id="calc_rabatt" type="Hidden">
<contact.accountType>
<grenze calc_value="1">1</grenze>
<grenze calc_value="0.8">2</grenze>
<grenze calc_value="0.5">3</grenze>
</contact.accountType>
</option>
<option id="calc" type="Hidden">
<auflage>
<grenze formel="0.21*$Vauflage$V*$CVcalc_rabatt_contact.accountType$CV">1-</grenze>
</auflage>
</option>
</artikel>
</kalkulation>

View File

@ -0,0 +1,235 @@
<?php
namespace Plugins\System\PSC\XmlCalc\Api;
use PSC\Shop\ContactBundle\Model\AccountType;
use PSC\Shop\EntityBundle\Repository\JobRepository;
use Tests\RefreshDatabaseTrait;
use PSC\Shop\ContactBundle\Repository\ContactRepository;
use PSC\Shop\EntityBundle\Entity\Shop;
use PSC\Shop\EntityBundle\Repository\ShopRepository;
use PSC\Shop\PaymentBundle\Repository\PaymentRepository;
use PSC\Shop\ShippingBundle\Repository\ShippingRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class CreateOrderTest extends WebTestCase
{
use RefreshDatabaseTrait;
public function testCreateOrderContactPersonal(): void
{
$client = static::createClient();
$shopRepository = static::getContainer()->get(ShopRepository::class);
$shop = $shopRepository->findOneBy(['title' => 'Printchampion']);
$shippingRepository = static::getContainer()->get(ShippingRepository::class);
$paymentRepository = static::getContainer()->get(PaymentRepository::class);
$client->jsonRequest(
'POST',
'/api/order/create',
[
'shop' => [
'uuid' => (string)$shop->getUuid()
],
'type' => 2,
'shipping' => [
'uid' => $shippingRepository->findOneBy(['title' => 'Abholung vor Ort'])->getUid()
],
'payment' => [
'uid' => $paymentRepository->findOneBy(['title' => 'Bar bei Abholung'])->getUid()
],
'draft' => false,
'deliveryAddress' => [
'firstname' => 'Thomas',
'lastname' => 'Peterson',
'street' => 'Chausseestr.',
'houseNumber' => '24',
'zip' => '17506',
'city' => 'Gribow'
],
'invoiceAddress' => [
'firstname' => 'Thomas',
'lastname' => 'Peterson',
'street' => 'Chausseestr.',
'houseNumber' => '24',
'zip' => '17400',
'city' => 'Berlin'
],
'positions' => [
[
'count' => 1,
'product' => [
'title' => 'test XML',
'specialProductTypeObject' => [
'typ' => 6,
'taxClass' => 1900,
'xml' => '<?xml version="1.0" encoding="utf-8"?>
<kalkulation>
<artikel>
<name>SD-Durchschreibesätze A4-Blocks</name>
<kommentar>210 mm x 297 mm</kommentar>
<option id="auflage" name="Auflage" type="Input" default="10"/>
<option id="calc_rabatt" type="Hidden">
<contact.accountType>
<grenze calc_value="1">1</grenze>
<grenze calc_value="0.8">2</grenze>
<grenze calc_value="0.5">3</grenze>
</contact.accountType>
</option>
<option id="calc" type="Hidden">
<auflage>
<grenze formel="10*$Vauflage$V*$CVcalc_rabatt_contact.accountType$CV">1-</grenze>
</auflage>
</option>
</artikel>
</kalkulation>'
]
]
]
]
],
['HTTP_apiKey' => $shop->getApiKey()]
);
self::assertSame(200, $client->getResponse()->getStatusCode());
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(11900, $data['gross']);
$client->jsonRequest(
'POST',
'/api/order/getonebyuuid',
[
'uuid' => $data['uuid'],
],
['HTTP_apiKey' => $shop->getApiKey()]
);
self::assertSame(200, $client->getResponse()->getStatusCode());
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(11900, $data['gross']);
$jobs = static::getContainer()->get(JobRepository::class);
self::assertCount(0, $jobs->findBy(['data.order' => $data['uuid']]));
}
public function testCreateOrderContactCompany(): void
{
$client = static::createClient();
$shopRepository = static::getContainer()->get(ShopRepository::class);
$shop = $shopRepository->findOneBy(['title' => 'Printchampion']);
$shippingRepository = static::getContainer()->get(ShippingRepository::class);
$paymentRepository = static::getContainer()->get(PaymentRepository::class);
$client->jsonRequest(
'POST',
'/api/order/create',
[
'shop' => [
'uuid' => (string)$shop->getUuid()
],
'type' => 2,
'shipping' => [
'uid' => $shippingRepository->findOneBy(['title' => 'Abholung vor Ort'])->getUid()
],
'payment' => [
'uid' => $paymentRepository->findOneBy(['title' => 'Bar bei Abholung'])->getUid()
],
'contact' => [
'accountType' => AccountType::COMPANY
],
'draft' => false,
'deliveryAddress' => [
'firstname' => 'Thomas',
'lastname' => 'Peterson',
'street' => 'Chausseestr.',
'houseNumber' => '24',
'zip' => '17506',
'city' => 'Gribow'
],
'invoiceAddress' => [
'firstname' => 'Thomas',
'lastname' => 'Peterson',
'street' => 'Chausseestr.',
'houseNumber' => '24',
'zip' => '17400',
'city' => 'Berlin'
],
'positions' => [
[
'count' => 1,
'product' => [
'title' => 'test XML',
'specialProductTypeObject' => [
'typ' => 6,
'taxClass' => 1900,
'xml' => '<?xml version="1.0" encoding="utf-8"?>
<kalkulation>
<artikel>
<name>SD-Durchschreibesätze A4-Blocks</name>
<kommentar>210 mm x 297 mm</kommentar>
<option id="auflage" name="Auflage" type="Input" default="10"/>
<option id="calc_rabatt" type="Hidden">
<contact.accountType>
<grenze calc_value="1">1</grenze>
<grenze calc_value="0.8">2</grenze>
<grenze calc_value="0.5">3</grenze>
</contact.accountType>
</option>
<option id="calc" type="Hidden">
<auflage>
<grenze formel="10*$Vauflage$V*$CVcalc_rabatt_contact.accountType$CV">1-</grenze>
</auflage>
</option>
</artikel>
</kalkulation>'
]
]
]
]
],
['HTTP_apiKey' => $shop->getApiKey()]
);
self::assertSame(200, $client->getResponse()->getStatusCode());
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(9520, $data['gross']);
$client->jsonRequest(
'POST',
'/api/order/getonebyuuid',
[
'uuid' => $data['uuid'],
],
['HTTP_apiKey' => $shop->getApiKey()]
);
self::assertSame(200, $client->getResponse()->getStatusCode());
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(9520, $data['gross']);
$jobs = static::getContainer()->get(JobRepository::class);
self::assertCount(0, $jobs->findBy(['data.order' => $data['uuid']]));
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace Plugins\System\PSC\XmlCalc\Api;
use PSC\Shop\ContactBundle\Repository\ContactRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Tests\RefreshDatabaseTrait;
class GetPriceTest extends WebTestCase
{
use RefreshDatabaseTrait;
public function testGetPriceWithoutUser(): void
{
$client = static::createClient();
$client->jsonRequest('POST', '/api/plugin/system/psc/xmlcalc/price', ['product' => '01938686-0e4d-7da9-bae3-b2e1b1681f9f'], []);
$this->assertResponseIsSuccessful();
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(2600, $data['netto']);
}
public function testGetPriceWithCompany(): void
{
$client = static::createClient();
$userRepository = static::getContainer()->get(ContactRepository::class);
$testUser = $userRepository->loadUserByUsername('company@shop.de');
$client->loginUser($testUser, 'api');
$client->jsonRequest('POST', '/api/plugin/system/psc/xmlcalc/price', ['product' => '01938686-0e4d-7da9-bae3-b2e1b1681f9f'], []);
$this->assertResponseIsSuccessful();
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(2080, $data['netto']);
}
public function testGetPriceWithAssociation(): void
{
$client = static::createClient();
$userRepository = static::getContainer()->get(ContactRepository::class);
$testUser = $userRepository->loadUserByUsername('association@shop.de');
$client->loginUser($testUser, 'api');
$client->jsonRequest('POST', '/api/plugin/system/psc/xmlcalc/price', ['product' => '01938686-0e4d-7da9-bae3-b2e1b1681f9f'], []);
$this->assertResponseIsSuccessful();
$data = json_decode($client->getResponse()->getContent(), true);
self::assertSame(1300, $data['netto']);
}
}

View File

@ -7,6 +7,8 @@ namespace Tests;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\EntityManagerInterface;
use Hautelook\AliceBundle\PhpUnit\BaseDatabaseTrait;
use PSC\Shop\ContactBundle\Model\AccountType;
use PSC\Shop\EntityBundle\Document\Contact;
use PSC\Shop\EntityBundle\Document\Country;
use PSC\Shop\EntityBundle\Document\Instance;
use PSC\Shop\EntityBundle\Document\Order;
@ -125,6 +127,26 @@ trait RefreshDatabaseTrait
$doc->flush();
$doc->clear();
}
$contactEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Contact::class)->findOneBy(['username' => 'company@shop.de']);
if (!$doc->getRepository(Contact::class)->findOneBy(['uid' => (string)$contactEntity->getUid()])) {
$contact = new Contact();
$contact->setAccountType(AccountType::COMPANY);
$contact->setUid((string)$contactEntity->getUid());
$doc->persist($contact);
$doc->flush();
$doc->clear();
}
$contactEntity = $em->getRepository(\PSC\Shop\EntityBundle\Entity\Contact::class)->findOneBy(['username' => 'association@shop.de']);
if (!$doc->getRepository(Contact::class)->findOneBy(['uid' => (string)$contactEntity->getUid()])) {
$contact = new Contact();
$contact->setAccountType(AccountType::ASSOCIATION);
$contact->setUid((string)$contactEntity->getUid());
$doc->persist($contact);
$doc->flush();
$doc->clear();
}
return $kernel;
}

View File

@ -36,6 +36,18 @@
</style>
</head>
<body class="w-screen h-screen flex flex-col">
<div class="absolute bg-white bg-opacity-60 z-10 h-full w-full flex items-center justify-center loadingScreen" >
<div class="flex items-center">
<span class="text-3xl mr-4">Loading</span>
<svg class="animate-spin h-8 w-8 text-gray-800" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
</div>
</div>
<div class="w-screen h-screen flex flex-col">
<div class="flex flex-row bg-gray-100 sticky top-0 mb-2 p-1">
<a class="basis-1/3 pl-1 font-bold text-2xl">{{ product.title }}</a>
@ -98,9 +110,15 @@
$('.save').click(function() {
$('.save').prop('disabled', true);
var arr = $("#customerForm").serializeArray();
$.each($("input:checkbox:not(:checked)"), function(i, e) {
$.each($("input:checkbox"), function(i, e) {
if(!$(this).is(':checked')) {
arr.push({name: e.name, value: "0"});
}else{
arr.push({name: e.name, value: "1"});
}
});
$('.loadingScreen').removeClass('hidden');
$.ajax({
method: 'PUT',
url: '/apps/api/plugin/custom/psc/collectlayouter/{% if mode == 3 %}savenewcontact{% else %}savecontact{% endif %}/' + productUUId,
@ -134,6 +152,7 @@
$('.pleasewait').html('Bitte Warten');
$('.loadingScreen').removeClass('hidden');
var dataObj = $.param({uuid: '{{ product.uuid }}', modus: 102, layouter_uuid: '{{ layouterUuid }}' });
$.ajax({
method: 'POST',
@ -142,8 +161,12 @@
dataType: "json",
success: function (result) {
var arr = $("#customerForm").serializeArray();
$.each($("input:checkbox:not(:checked)"), function(i, e) {
$.each($("input:checkbox"), function(i, e) {
if(!$(this).is(':checked')) {
arr.push({name: e.name, value: "0"});
}else{
arr.push({name: e.name, value: "1"});
}
});
$.ajax({
@ -476,7 +499,7 @@
var $group = $('<div class="input-group input-group-sm"></div>');
}
var $groupText = $('<div class="input-group-text"></div>');
var $check = $('<input class="form-check-input mt-0" type="hidden" name="data[' + element.id + '][enable]" value="1">');
var $check = $('<input class="form-check-input mt-0" type="hidden" name="data[' + element.id + '][enable]" value="0">');
var $message = $('<div class="required hidden text-lime-700">Ist ein Pflichfeld </div>');
@ -484,7 +507,7 @@
if(element.required) {
$message.appendTo($group);
}
$check.appendTo($container);
//$check.appendTo($container);
$label.appendTo($container);
$group.appendTo($col);
$col.appendTo($container);
@ -523,6 +546,8 @@
preview();
bindImageUploaderFiles();
bindCropper();
$('.loadingScreen').addClass("hidden");
}
});
}
@ -530,8 +555,12 @@
function preview() {
$('.previews').html('<h5 class="bg-info" >Loading</h5>');
var arr = $("#customerForm").serializeArray();
$.each($("input:checkbox:not(:checked)"), function(i, e) {
$.each($("input:checkbox"), function(i, e) {
if(!$(this).is(':checked')) {
arr.push({name: e.name, value: "0"});
}else{
arr.push({name: e.name, value: "1"});
}
});
var cont = "";

View File

@ -36,6 +36,18 @@
</style>
</head>
<body class="w-screen h-screen flex flex-col">
<div class="absolute bg-white bg-opacity-60 z-10 h-full w-full flex items-center justify-center loadingScreen" >
<div class="flex items-center">
<span class="text-3xl mr-4">Loading</span>
<svg class="animate-spin h-8 w-8 text-gray-800" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
</div>
</div>
<div class="w-screen h-screen flex flex-col">
<div class="flex flex-row bg-gray-100 sticky top-0 mb-2 p-1">
<a class="basis-1/3 pl-1 font-bold text-2xl">{{ product.title }}</a>
@ -479,6 +491,7 @@
preview();
bindImageUploaderFiles();
bindCropper();
$('.loadingScreen').addClass("hidden");
}
});
}

View File

@ -36,6 +36,18 @@
</style>
</head>
<body class="w-screen h-screen flex flex-col">
<div class="absolute bg-white bg-opacity-60 z-10 h-full w-full flex items-center justify-center loadingScreen" >
<div class="flex items-center">
<span class="text-3xl mr-4">Loading</span>
<svg class="animate-spin h-8 w-8 text-gray-800" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
</div>
</div>
<div class="w-screen h-screen flex flex-col">
<div class="flex flex-row bg-gray-100 sticky top-0 mb-2 p-1">
<a class="basis-1/3 pl-1 font-bold text-2xl">{{ product.title }}</a>
@ -108,8 +120,12 @@
dataType: "json",
success: function (result) {
var arr = $("#customerForm").serializeArray();
$.each($("input:checkbox:not(:checked)"), function(i, e) {
$.each($("input:checkbox"), function(i, e) {
if(!$(this).is(':checked')) {
arr.push({name: e.name, value: "0"});
}else{
arr.push({name: e.name, value: "1"});
}
});
$.ajax({
@ -432,7 +448,7 @@
var $group = $('<div class="input-group input-group-sm"></div>');
}
var $groupText = $('<div class="input-group-text"></div>');
var $check = $('<input class="form-check-input mt-0" type="hidden" name="data[' + element.id + '][enable]" value="1">');
var $check = $('<input class="form-check-input mt-0" type="hidden" name="data[' + element.id + '][enable]" value="0">');
var $message = $('<div class="required hidden text-lime-700">Ist ein Pflichfeld </div>');
@ -440,7 +456,7 @@
if(element.required) {
$message.appendTo($group);
}
$check.appendTo($container);
// $check.appendTo($container);
$label.appendTo($container);
$group.appendTo($col);
$col.appendTo($container);
@ -479,6 +495,7 @@
preview();
bindImageUploaderFiles();
bindCropper();
$('.loadingScreen').addClass("hidden");
}
});
}
@ -486,8 +503,12 @@
function preview() {
$('.previews').html('<h5 class="bg-info" >Loading</h5>');
var arr = $("#customerForm").serializeArray();
$.each($("input:checkbox:not(:checked)"), function(i, e) {
$.each($("input:checkbox"), function(i, e) {
if(!$(this).is(':checked')) {
arr.push({name: e.name, value: "0"});
}else{
arr.push({name: e.name, value: "1"});
}
});
var cont = "";

View File

@ -27,6 +27,8 @@ class StartController extends AbstractController
#[Route('/start/{id}', name: 'plugin_custom_psc_formular_frontend_start')]
public function startAction(Request $request, string $id)
{
$selectedShop = $this->shopService->getShopByDomain();
$shopDoc = $this->shopService->getMongoShopByDomain();
@ -44,6 +46,10 @@ class StartController extends AbstractController
$web2mailreCaptchawebcode = $formular->getReCaptchawebcode();
$web2mailreCaptchasecret = $formular->getReCaptchasecret();
if($formular->isPrivate() && $this->getUser() == null) {
return $this->redirect('/');
}
$web2maileditor = $formular->getEditor();
$send = false;

View File

@ -31,6 +31,9 @@ class Formular
#[Field(type: 'string')]
private $formularname;
#[Field(type: 'boolean')]
private bool $private = false;
#[Field(type: 'boolean')]
private $reCaptcha;
@ -55,6 +58,7 @@ class Formular
public function __construct()
{
$this->captcha = true;
$this->private = false;
}
/**
@ -211,4 +215,14 @@ class Formular
$this->captchaFox = $captchaFox;
}
public function isPrivate(): bool
{
return (bool)$this->private;
}
public function setPrivate(bool $private): void
{
$this->private = $private;
}
}

View File

@ -49,6 +49,10 @@ class Formular extends AbstractType
'label' => 'Formularname',
'required' => false,
));
$builder->add('private', CheckboxType::class, array(
'label' => 'Nur für Angemeldete Nutzer',
'required' => false
));
$builder->add('captcha', CheckboxType::class, array(
'label' => 'Captcha nutzen (nach DSGVO)?',
'required' => false

View File

@ -33,6 +33,18 @@
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<div class="form-group row">
<div class="col-md-12">
{{ form_label(form.private ) }}
</div>
<div class="col-md-12">
{{ form_widget(form.private) }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<div class="form-group row">

View File

@ -33,6 +33,18 @@
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<div class="form-group row">
<div class="col-md-12">
{{ form_label(form.private ) }}
</div>
<div class="col-md-12">
{{ form_widget(form.private) }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-2">
<div class="form-group row">

View File

@ -72,7 +72,7 @@ class BackendFields extends \PSC\System\PluginBundle\Form\Field implements Field
$builder
->add('uploadPitchprint', CheckboxType::class, ['required' => false, 'label' => 'Aktivieren'])
->add("uploadPitchprintInitalStatus", ChoiceType::class, array(
'label' => 'Status',
'label' => 'Initalstatus',
'choices' => $this->statusService->getPositionStatusAsArray(),
'translation_domain' => 'posstatus'
))

View File

@ -36,7 +36,7 @@
parent.document.getElementById('product_pitchprint_uploadPitchprintDesignId').value = id;
parent.document.getElementById('product_pitchprint_uploadPitchprintDesignPreviews').value = previews;
var useDesignModal = parent.document.getElementById('useDesign');
parent.loadChoosenDesign();
parent.loadChoosenDesign1();
parent.$(useDesignModal).modal('hide');
}
</script>

View File

@ -1,5 +1,5 @@
<script>
function loadChoosenDesign() {
function loadChoosenDesign1() {
if($("#product_pitchprint_uploadPitchprintDesignPreviews").val() != "") {
$(".chooseDesignPreview").html('<img src="' + $("#product_pitchprint_uploadPitchprintDesignPreviews").val().split(';')[0] + '" style="max-height: 200px;" />');
}
@ -61,5 +61,7 @@ Designauswahl
</div>
</div>
<script>
window.onload = function() {loadChoosenDesign()};
document.addEventListener("DOMContentLoaded", (event) => {
loadChoosenDesign1()
});
</script>

View File

@ -37,7 +37,7 @@ class ProductSettings extends \PSC\System\PluginBundle\Form\Field implements Fie
$builder
->add('uploadPrintess', CheckboxType::class, ['required' => false, 'label' => 'Aktivieren'])
->add("uploadPrintessInitalStatus", ChoiceType::class, array(
'label' => 'Status',
'label' => 'Initalstatus',
'choices' => $this->statusService->getPositionStatusAsArray(),
'translation_domain' => 'posstatus'
))

View File

@ -12,7 +12,7 @@
</div>
</div>
</div>
<div class="col-md-7">
<div class="col-md-4">
<div class="form-group row">
{{ form_label(form.printess.uploadPrintessInitalStatus) }}
<div class="col-md-8">
@ -20,6 +20,14 @@
</div>
</div>
</div>
<div class="col-md-3">
<div class="form-group row">
{{ form_label(form.printess.uploadPrintessDownload) }}
<div class="col-md-8">
{{ form_widget(form.printess.uploadPrintessDownload) }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-9">
@ -32,14 +40,6 @@
</div>
</div>
</div>
<div class="col-md-3">
<div class="form-group row">
{{ form_label(form.printess.uploadPrintessDownload) }}
<div class="col-md-8">
{{ form_widget(form.printess.uploadPrintessDownload) }}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -8,7 +8,7 @@ use function array_merge;
abstract class Base {
private string $apiUrl = 'https://api.print.app/runtime/';
private string $apiUrl = 'https://api.print.app/v1/';
private Shop $shopService;
protected HttpClientInterface $httpClient;
@ -21,7 +21,7 @@ abstract class Base {
protected function makeGetCall(string $url): array
{
$response = $this->httpClient->request('GET', $this->apiUrl . $url, ['headers' => ['Authorization' => $this->shopService->getMongoSelectedShop()->getPluginSettingModule('pitchprint_r2', 'authKey')]]);
$response = $this->httpClient->request('GET', $this->apiUrl . $url, ['headers' => ['Authorization' => 'Bearer ' .$this->shopService->getMongoSelectedShop()->getPluginSettingModule('pitchprint_r2', 'authKey')]]);
if($response->getStatusCode() == 200) {
return $response->toArray();

View File

@ -9,6 +9,7 @@
* @license Private
* @link http://www.printshopcreator.de
*/
namespace Plugin\Custom\PSC\R2_Pitchprint\Controller\Backend;
use Doctrine\ODM\MongoDB\DocumentManager;
@ -22,6 +23,10 @@ use Symfony\Component\Routing\Annotation\Route;
class DesignController extends AbstractController
{
public function __construct(private Design $designService)
{
}
#[Template()]
#[Route('/design/edit/{uuid}/{folder}', name: 'psc_plugin_pitchprint_r2_backend_design_edit')]
public function editAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, DocumentManager $documentManager, EntityManagerInterface $entityManager, Design $design, $uuid, $folder = "")
@ -35,11 +40,16 @@ class DesignController extends AbstractController
$output = $design->getDesigns($folder);
$data = [];
if(isset($output['data']) && isset($output['data']['id']) && $output['data']['id'] == 'root') {
$data = $output['data']['items'];
}else{
$data = $output['data'];
$data = ['folders' => [], 'designs' => []];
if (isset($output['root']) && isset($output['root']['id']) && $output['root']['id'] == 'root') {
foreach ($output['root']['items'] as $item) {
$data = $this->getItems($data, $item);
}
}
if (isset($output['data'])) {
foreach ($output['data']['items'] as $item) {
$data = $this->getItems($data, $item);
}
}
return array(
@ -48,4 +58,17 @@ class DesignController extends AbstractController
);
}
private function getItems($data, $folder): array
{
$itemResult = $this->designService->getDesigns($folder);
if (isset($itemResult['data']['unit'])) {
$data['designs'][] = $itemResult['data'];
}
if (!isset($itemResult['data']['unit']) && isset($itemResult['data']['items'])) {
$data['folders'][] = $itemResult['data'];
}
return $data;
}
}

View File

@ -34,12 +34,12 @@ class BackendFields extends \PSC\System\PluginBundle\Form\Field implements Field
$builder
->add('uploadPitchprint', CheckboxType::class, ['required' => false, 'label' => 'Aktivieren'])
->add("uploadPitchprintInitalStatus", ChoiceType::class, array(
'label' => 'Status',
'label' => 'Initalstatus',
'choices' => $this->statusService->getPositionStatusAsArray(),
'translation_domain' => 'posstatus'
))
->add('uploadPitchprintDownload', CheckboxType::class, ['required' => false, 'label' => 'Download'])
->add('uploadPitchprintDesignId', HiddenType::class)
->add('uploadPitchprintDesignId', TextType::class, ['label' => 'Design', 'required' => false])
->add('uploadPitchprintDesignPreviews', HiddenType::class);
return $builder;

View File

@ -2,8 +2,7 @@
{% block body %}
<div class="row">
{% for design in designs %}
{% if design.items is defined %}
{% for design in designs.folders %}
<div class="col-1 m-2">
<div class="card h-100">
<div class="card-body">
@ -11,9 +10,9 @@
<a href="{{ path('psc_plugin_pitchprint_r2_backend_design_edit', {uuid: product.uuid, folder: design.id}) }}">{{ design.title }}</a>
</div>
</div>
</div>
{% else %}
{% endfor %}
{% for design in designs.designs %}
<div class="col-1 m-2">
<div class="card h-100">
<img src="{{ design.pages[0]['preview'] }}" class="card-img-top">
@ -26,7 +25,6 @@
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
<script>
@ -35,7 +33,7 @@
parent.document.getElementById('product_pitchprint_r2_uploadPitchprintDesignId').value = id;
parent.document.getElementById('product_pitchprint_r2_uploadPitchprintDesignPreviews').value = previews;
var useDesignModal = parent.document.getElementById('useDesignR2');
parent.loadChoosenDesign();
parent.loadChoosenDesign2();
parent.$(useDesignModal).modal('hide');
}
</script>

View File

@ -1,5 +1,5 @@
<script>
function loadChoosenDesign() {
function loadChoosenDesign2() {
if($("#product_pitchprint_r2_uploadPitchprintDesignPreviews").val() != "") {
$(".chooseDesignPreviewR2").html('<img src="' + $("#product_pitchprint_r2_uploadPitchprintDesignPreviews").val().split(';')[0] + '" style="max-height: 200px;" />');
}
@ -38,14 +38,25 @@
</div>
<div class="row">
<div class="col-12">
<div class="form-group row">
{{ form_label(form.pitchprint_r2.uploadPitchprintDesignId) }}
<div class="col-md-8">
{{form_widget(form.pitchprint_r2.uploadPitchprintDesignId)}}
</div>
</div>
</div>
</div>
<div class="row mt-1">
{{form_widget(form.pitchprint_r2.uploadPitchprintDesignPreviews)}}
<div class="col-md-4">
<div class="col-2">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#useDesignR2">
Designauswahl
</button>
</div>
<div class="col-4">
</div>
<div class="col-6">
<div class="chooseDesignPreviewR2"></div>
</div>
@ -66,5 +77,7 @@
</div>
</div>
<script>
window.onload = function() {loadChoosenDesign()};
document.addEventListener("DOMContentLoaded", (event) => {
loadChoosenDesign2()
});
</script>

View File

@ -33,6 +33,7 @@ class PutConfig extends Base
$result = $this->put(sprintf("config/%s/%s/configurations", $this->customerNumber, $this->getProductId()), $this->buildData());
var_dump($result);
$tmp = ['property' => [], 'custom' => []];
foreach($result['ValidOptions'] as $key => $value) {

View File

@ -7,7 +7,6 @@ use Doctrine\ORM\EntityManagerInterface;
use Plugin\Custom\PSC\Saxoprint_API_R1\Api\GetConfig;
use Plugin\Custom\PSC\Saxoprint_API_R1\Api\GetPrice;
use Plugin\Custom\PSC\Saxoprint_API_R1\Api\GetPrices;
use Plugin\Custom\PSC\Saxoprint_API_R1\Api\PutConfig;
use PSC\Shop\EntityBundle\Entity\Product;
use PSC\System\SettingsBundle\Service\Help;
@ -15,7 +14,6 @@ use PSC\System\SettingsBundle\Service\Shop;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
@ -128,7 +126,8 @@ class SaxoprintController extends AbstractController
$putConfig->setConfig($reqConfig);
$config = $putConfig->call();
}
var_dump($config);
die();
$getPrices->setShop($shop->getMongoShopByUid($product->getShop()->getUid()));
$getPrices->setProductId((int)$request->get('saxoprintProductId'));
@ -300,7 +299,7 @@ class SaxoprintController extends AbstractController
$tmp = [];
foreach ($config as $conf) {
if($help = $this->helpService->getHelp($conf['id'])) {
if ($help = $this->helpService->getHelp((string)$product->getUid(), $conf['id'])) {
$conf['help'] = $help->asArray();
} else {
$conf['help'] = false;

View File

@ -5,17 +5,25 @@
"npm:@types/react-dom@^18.3.0": "18.3.1",
"npm:@types/react@^18.3.10": "18.3.11",
"npm:@vitejs/plugin-react@^4.3.2": "4.3.2_vite@5.4.8_@babel+core@7.25.8",
"npm:autoprefixer@^10.4.20": "10.4.20_postcss@8.4.47",
"npm:axios@^1.7.7": "1.7.7",
"npm:eslint-plugin-react-hooks@^5.1.0-rc.0": "5.1.0-rc-fb9a90fa48-20240614_eslint@9.12.0",
"npm:eslint-plugin-react-refresh@~0.4.12": "0.4.12_eslint@9.12.0",
"npm:eslint@^9.11.1": "9.12.0",
"npm:globals@^15.9.0": "15.11.0",
"npm:postcss@^8.4.47": "8.4.47",
"npm:react-dom@^18.3.1": "18.3.1_react@18.3.1",
"npm:react-router-dom@^6.27.0": "6.27.0_react@18.3.1_react-dom@18.3.1__react@18.3.1",
"npm:react@^18.3.1": "18.3.1",
"npm:tailwindcss@^3.4.13": "3.4.13_postcss@8.4.47",
"npm:typescript-eslint@^8.7.0": "8.8.1_@typescript-eslint+parser@8.8.1__eslint@9.12.0__typescript@5.6.3_eslint@9.12.0_typescript@5.6.3",
"npm:typescript@^5.5.3": "5.6.3",
"npm:vite@^5.4.8": "5.4.8"
},
"npm": {
"@alloc/quick-lru@5.2.0": {
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
},
"@ampproject/remapping@2.3.0": {
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dependencies": [
@ -68,7 +76,7 @@
"@babel/compat-data",
"@babel/helper-validator-option",
"browserslist",
"lru-cache",
"lru-cache@5.1.1",
"semver@6.3.1"
]
},
@ -304,6 +312,17 @@
"@humanwhocodes/retry@0.3.1": {
"integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="
},
"@isaacs/cliui@8.0.2": {
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dependencies": [
"string-width@5.1.2",
"string-width-cjs@npm:string-width@4.2.3",
"strip-ansi@7.1.0",
"strip-ansi-cjs@npm:strip-ansi@6.0.1",
"wrap-ansi@8.1.0",
"wrap-ansi-cjs@npm:wrap-ansi@7.0.0"
]
},
"@jridgewell/gen-mapping@0.3.5": {
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dependencies": [
@ -345,6 +364,12 @@
"fastq"
]
},
"@pkgjs/parseargs@0.11.0": {
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="
},
"@remix-run/router@1.20.0": {
"integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg=="
},
"@rollup/rollup-android-arm-eabi@4.24.0": {
"integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA=="
},
@ -549,6 +574,12 @@
"uri-js"
]
},
"ansi-regex@5.0.1": {
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-regex@6.1.0": {
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="
},
"ansi-styles@3.2.1": {
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": [
@ -561,12 +592,54 @@
"color-convert@2.0.1"
]
},
"ansi-styles@6.2.1": {
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
},
"any-promise@1.3.0": {
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
},
"anymatch@3.1.3": {
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dependencies": [
"normalize-path",
"picomatch"
]
},
"arg@5.0.2": {
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"argparse@2.0.1": {
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"asynckit@0.4.0": {
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"autoprefixer@10.4.20_postcss@8.4.47": {
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
"dependencies": [
"browserslist",
"caniuse-lite",
"fraction.js",
"normalize-range",
"picocolors",
"postcss",
"postcss-value-parser"
]
},
"axios@1.7.7": {
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"dependencies": [
"follow-redirects",
"form-data",
"proxy-from-env"
]
},
"balanced-match@1.0.2": {
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"binary-extensions@2.3.0": {
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
},
"brace-expansion@1.1.11": {
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": [
@ -598,6 +671,9 @@
"callsites@3.1.0": {
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"camelcase-css@2.0.1": {
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
},
"caniuse-lite@1.0.30001668": {
"integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw=="
},
@ -616,6 +692,19 @@
"supports-color@7.2.0"
]
},
"chokidar@3.6.0": {
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dependencies": [
"anymatch",
"braces",
"fsevents",
"glob-parent@5.1.2",
"is-binary-path",
"is-glob",
"normalize-path",
"readdirp"
]
},
"color-convert@1.9.3": {
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": [
@ -634,6 +723,15 @@
"color-name@1.1.4": {
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"combined-stream@1.0.8": {
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": [
"delayed-stream"
]
},
"commander@4.1.1": {
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
},
"concat-map@0.0.1": {
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
@ -648,6 +746,9 @@
"which"
]
},
"cssesc@3.0.0": {
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
},
"csstype@3.1.3": {
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
@ -660,9 +761,27 @@
"deep-is@0.1.4": {
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
},
"delayed-stream@1.0.0": {
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"didyoumean@1.2.2": {
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"dlv@1.1.3": {
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"eastasianwidth@0.2.0": {
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
},
"electron-to-chromium@1.5.36": {
"integrity": "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw=="
},
"emoji-regex@8.0.0": {
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"emoji-regex@9.2.2": {
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
},
"esbuild@0.21.5": {
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dependencies": [
@ -845,9 +964,33 @@
"flatted@3.3.1": {
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
},
"follow-redirects@1.15.9": {
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
},
"foreground-child@3.3.0": {
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
"dependencies": [
"cross-spawn",
"signal-exit"
]
},
"form-data@4.0.1": {
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": [
"asynckit",
"combined-stream",
"mime-types"
]
},
"fraction.js@4.3.7": {
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="
},
"fsevents@2.3.3": {
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
},
"function-bind@1.1.2": {
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"gensync@1.0.0-beta.2": {
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
},
@ -863,6 +1006,17 @@
"is-glob"
]
},
"glob@10.4.5": {
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dependencies": [
"foreground-child",
"jackspeak",
"minimatch@9.0.5",
"minipass",
"package-json-from-dist",
"path-scurry"
]
},
"globals@11.12.0": {
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
},
@ -881,6 +1035,12 @@
"has-flag@4.0.0": {
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"hasown@2.0.2": {
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": [
"function-bind"
]
},
"ignore@5.3.2": {
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
},
@ -894,9 +1054,24 @@
"imurmurhash@0.1.4": {
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
},
"is-binary-path@2.1.0": {
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dependencies": [
"binary-extensions"
]
},
"is-core-module@2.15.1": {
"integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
"dependencies": [
"hasown"
]
},
"is-extglob@2.1.1": {
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
},
"is-fullwidth-code-point@3.0.0": {
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-glob@4.0.3": {
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dependencies": [
@ -909,6 +1084,16 @@
"isexe@2.0.0": {
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"jackspeak@3.4.3": {
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dependencies": [
"@isaacs/cliui",
"@pkgjs/parseargs"
]
},
"jiti@1.21.6": {
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w=="
},
"js-tokens@4.0.0": {
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
@ -946,6 +1131,15 @@
"type-check"
]
},
"lilconfig@2.1.0": {
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="
},
"lilconfig@3.1.2": {
"integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow=="
},
"lines-and-columns@1.2.4": {
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"locate-path@6.0.0": {
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dependencies": [
@ -961,6 +1155,9 @@
"js-tokens"
]
},
"lru-cache@10.4.3": {
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
},
"lru-cache@5.1.1": {
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dependencies": [
@ -977,6 +1174,15 @@
"picomatch"
]
},
"mime-db@1.52.0": {
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types@2.1.35": {
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": [
"mime-db"
]
},
"minimatch@3.1.2": {
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": [
@ -989,9 +1195,20 @@
"brace-expansion@2.0.1"
]
},
"minipass@7.1.2": {
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
},
"ms@2.1.3": {
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"mz@2.7.0": {
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dependencies": [
"any-promise",
"object-assign",
"thenify-all"
]
},
"nanoid@3.3.7": {
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
},
@ -1001,6 +1218,18 @@
"node-releases@2.0.18": {
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
},
"normalize-path@3.0.0": {
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"normalize-range@0.1.2": {
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="
},
"object-assign@4.1.1": {
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"object-hash@3.0.0": {
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
},
"optionator@0.9.4": {
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dependencies": [
@ -1024,6 +1253,9 @@
"p-limit"
]
},
"package-json-from-dist@1.0.1": {
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
"parent-module@1.0.1": {
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dependencies": [
@ -1036,12 +1268,69 @@
"path-key@3.1.1": {
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse@1.0.7": {
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"path-scurry@1.11.1": {
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dependencies": [
"lru-cache@10.4.3",
"minipass"
]
},
"picocolors@1.1.0": {
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
},
"picomatch@2.3.1": {
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
},
"pify@2.3.0": {
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
},
"pirates@4.0.6": {
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="
},
"postcss-import@15.1.0_postcss@8.4.47": {
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dependencies": [
"postcss",
"postcss-value-parser",
"read-cache",
"resolve"
]
},
"postcss-js@4.0.1_postcss@8.4.47": {
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dependencies": [
"camelcase-css",
"postcss"
]
},
"postcss-load-config@4.0.2_postcss@8.4.47": {
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dependencies": [
"lilconfig@3.1.2",
"postcss",
"yaml"
]
},
"postcss-nested@6.2.0_postcss@8.4.47": {
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dependencies": [
"postcss",
"postcss-selector-parser"
]
},
"postcss-selector-parser@6.1.2": {
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dependencies": [
"cssesc",
"util-deprecate"
]
},
"postcss-value-parser@4.2.0": {
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"postcss@8.4.47": {
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
"dependencies": [
@ -1053,6 +1342,9 @@
"prelude-ls@1.2.1": {
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
},
"proxy-from-env@1.1.0": {
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"punycode@2.3.1": {
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
},
@ -1070,15 +1362,51 @@
"react-refresh@0.14.2": {
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="
},
"react-router-dom@6.27.0_react@18.3.1_react-dom@18.3.1__react@18.3.1": {
"integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
"dependencies": [
"@remix-run/router",
"react",
"react-dom",
"react-router"
]
},
"react-router@6.27.0_react@18.3.1": {
"integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
"dependencies": [
"@remix-run/router",
"react"
]
},
"react@18.3.1": {
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"dependencies": [
"loose-envify"
]
},
"read-cache@1.0.0": {
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dependencies": [
"pify"
]
},
"readdirp@3.6.0": {
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": [
"picomatch"
]
},
"resolve-from@4.0.0": {
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
},
"resolve@1.22.8": {
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": [
"is-core-module",
"path-parse",
"supports-preserve-symlinks-flag"
]
},
"reusify@1.0.4": {
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
},
@ -1132,12 +1460,55 @@
"shebang-regex@3.0.0": {
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"signal-exit@4.1.0": {
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
},
"source-map-js@1.2.1": {
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
},
"string-width@4.2.3": {
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": [
"emoji-regex@8.0.0",
"is-fullwidth-code-point",
"strip-ansi@6.0.1"
]
},
"string-width@5.1.2": {
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dependencies": [
"eastasianwidth",
"emoji-regex@9.2.2",
"strip-ansi@7.1.0"
]
},
"strip-ansi@6.0.1": {
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": [
"ansi-regex@5.0.1"
]
},
"strip-ansi@7.1.0": {
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dependencies": [
"ansi-regex@6.1.0"
]
},
"strip-json-comments@3.1.1": {
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
},
"sucrase@3.35.0": {
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
"dependencies": [
"@jridgewell/gen-mapping",
"commander",
"glob",
"lines-and-columns",
"mz",
"pirates",
"ts-interface-checker"
]
},
"supports-color@5.5.0": {
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": [
@ -1150,9 +1521,51 @@
"has-flag@4.0.0"
]
},
"supports-preserve-symlinks-flag@1.0.0": {
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"tailwindcss@3.4.13_postcss@8.4.47": {
"integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==",
"dependencies": [
"@alloc/quick-lru",
"arg",
"chokidar",
"didyoumean",
"dlv",
"fast-glob",
"glob-parent@6.0.2",
"is-glob",
"jiti",
"lilconfig@2.1.0",
"micromatch",
"normalize-path",
"object-hash",
"picocolors",
"postcss",
"postcss-import",
"postcss-js",
"postcss-load-config",
"postcss-nested",
"postcss-selector-parser",
"resolve",
"sucrase"
]
},
"text-table@0.2.0": {
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
},
"thenify-all@1.6.0": {
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dependencies": [
"thenify"
]
},
"thenify@3.3.1": {
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dependencies": [
"any-promise"
]
},
"to-fast-properties@2.0.0": {
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
},
@ -1168,6 +1581,9 @@
"typescript"
]
},
"ts-interface-checker@0.1.13": {
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
"type-check@0.4.0": {
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dependencies": [
@ -1199,6 +1615,9 @@
"punycode"
]
},
"util-deprecate@1.0.2": {
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"vite@5.4.8": {
"integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
"dependencies": [
@ -1217,9 +1636,28 @@
"word-wrap@1.2.5": {
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
},
"wrap-ansi@7.0.0": {
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dependencies": [
"ansi-styles@4.3.0",
"string-width@4.2.3",
"strip-ansi@6.0.1"
]
},
"wrap-ansi@8.1.0": {
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dependencies": [
"ansi-styles@6.2.1",
"string-width@5.1.2",
"strip-ansi@7.1.0"
]
},
"yallist@3.1.1": {
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"yaml@2.6.0": {
"integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ=="
},
"yocto-queue@0.1.0": {
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
}
@ -1228,6 +1666,7 @@
"packageJson": {
"dependencies": [
"npm:@eslint/js@^9.11.1",
"npm:@hookform/resolvers@^3.9.0",
"npm:@types/react-dom@^18.3.0",
"npm:@types/react@^18.3.10",
"npm:@vitejs/plugin-react@^4.3.2",
@ -1239,12 +1678,18 @@
"npm:globals@^15.9.0",
"npm:postcss@^8.4.47",
"npm:react-dom@^18.3.1",
"npm:react-hook-form@^7.53.0",
"npm:react-router-dom@^6.27.0",
"npm:react-router@^6.27.0",
"npm:react-select-async-paginate@~0.7.6",
"npm:react-toastify@^10.0.6",
"npm:react@^18.3.1",
"npm:tailwindcss@^3.4.13",
"npm:typescript-eslint@^8.7.0",
"npm:typescript@^5.5.3",
"npm:vite@^5.4.8"
"npm:uuid@^11.0.1",
"npm:vite@^5.4.8",
"npm:yup@^1.4.0"
]
}
}

View File

@ -10,13 +10,20 @@
"preview": "vite preview"
},
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"autoprefixer": "^10.4.20",
"axios": "^1.7.7",
"postcss": "^8.4.47",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"react-router": "^6.27.0",
"react-router-dom": "^6.27.0",
"tailwindcss": "^3.4.13"
"react-select-async-paginate": "^0.7.6",
"react-toastify": "^10.0.6",
"tailwindcss": "^3.4.13",
"uuid": "^11.0.1",
"yup": "^1.4.0"
},
"devDependencies": {
"@eslint/js": "^9.11.1",

View File

@ -1,33 +0,0 @@
import { useState } from 'react'
import { createContext } from 'react';
import './assets/App.css'
import { BrowserRouter, Route, Routes } from "react-router-dom"
import Login from "./components/login/login"
interface AuthContextType {
jwt: string;
}
const AuthContext = createContext<AuthContextType|null>(null);
function App() {
const [jwt, setJWt] = useState(null)
return (
<>
<BrowserRouter>
<AuthContext.Provider value={{
auth: [jwt, setJWt],
}}>
<Routes>
<Route path='/' element={<Login />} />
<Route path='/create' element={<Login />} />
<Route path='/edit' element={<Login />} />
</Routes>
</AuthContext.Provider>
</BrowserRouter>
</>
)
}
export default App

View File

@ -0,0 +1,31 @@
import { Outlet } from "react-router";
import Navbar from "./components/navbar/navbar";
import './assets/app.css'
import { BrowserRouter, Route, Routes } from "react-router-dom"
import { ToastContainer } from "react-toastify";
import { UserProvider } from "./context/useAuth"
import { OrderProvider } from "./context/useOrder"
import globalRouter from "./helpers/globalRouter";
import { useLocation, useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
const location = useLocation();
globalRouter.navigate = navigate;
globalRouter.location = location;
return (
<>
<UserProvider>
<OrderProvider>
<Navbar />
<Outlet />
<ToastContainer />
</OrderProvider>
</UserProvider>
</>
)
}
export default App

View File

@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@ -1,3 +1,4 @@
@import 'react-toastify/dist/ReactToastify.css';
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,58 +1,106 @@
import React, { useState, useContext } from "react";
import axios from "axios";
import React from "react";
import * as Yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useAuth } from "../../context/useAuth";
import { useForm } from "react-hook-form";
import { useLocation } from "react-router-dom"
type Props = {};
function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [errorMessage, setErrorMessage] = useState(null);
const { setToken } = useContext(AuthContext);
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post("/apps/api/contact/login", {
username,
password,
});
setToken(response.data.token);
localStorage.setItem("token", response.data.token);
navigate("/dashboard");
} catch (error) {
console.error("Authentication failed:", error);
setToken(null);
localStorage.removeItem("token");
if (error.response && error.response.data) {
setErrorMessage(error.response.data); // Set the error message if present in the error response
} else {
setErrorMessage("An unexpected error occurred. Please try again.");
}
}
type LoginFormsInputs = {
userName: string;
password: string;
};
const validation = Yup.object().shape({
userName: Yup.string().required("Username is required"),
password: Yup.string().required("Password is required"),
});
const Login = (props: Props) => {
const { loginUser } = useAuth();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<LoginFormsInputs>({ resolver: yupResolver(validation) });
const location = useLocation();
const handleLogin = (form: LoginFormsInputs) => {
loginUser(form.userName, form.password, location.state.from);
};
return (
<>
<div className='bg-black/50 fixed top-0 left-0 w-full h-screen'></div>
<div className='fixed w-full px-4 py-24 z-50'>
<div className='max-w-[450px] h-[600px] mx-auto bg-black/80 text-white'>
<div className='max-w-[320px] mx-auto py-16'>
<h1>Sign Up Here</h1>
<form className='w-full flex flex-col py-4'>
<p className='text-white font-bold'>UserName</p>
<input type="text" required className='p-3 my-2 rounded text-black' placeholder='JohnDoe'/>
<p className='text-white font-bold'>PassWord</p>
<input type="password" required className='p-3 my-2 rounded text-black' placeholder='Please enter a strong password'/>
<button type="submit" className='bg-red-700 py-3 my-6 rounded font-bold px-4'>Submit</button>
<section className="bg-gray-50 dark:bg-gray-900">
<div className="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
<div className="w-full bg-white rounded-lg shadow dark:border md:mb-20 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
<div className="p-6 space-y-4 md:space-y-6 sm:p-8">
<h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
Sign in to your account
</h1>
<form
className="space-y-4 md:space-y-6"
onSubmit={handleSubmit(handleLogin)}
>
<div>
<p><input type="checkbox" />Remember Me</p>
<label
htmlFor="email"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Username
</label>
<input
type="text"
id="username"
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Username"
{...register("userName")}
/>
{errors.userName ? (
<p className="text-white">{errors.userName.message}</p>
) : (
""
)}
</div>
<div>
<label
htmlFor="password"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Password
</label>
<input
type="password"
id="password"
placeholder="••••••••"
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
{...register("password")}
/>
{errors.password ? (
<p className="text-white">{errors.password.message}</p>
) : (
""
)}
</div>
<div className="flex items-center justify-between">
<a
href="#"
className="text-sm text-white font-medium text-primary-600 hover:underline dark:text-primary-500"
>
Forgot password?
</a>
</div>
<button
type="submit"
className="w-full text-white bg-psc hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
>
Sign in
</button>
</form>
</div>
</div>
</div>
</>
)
}
</section>
);
};
export default Login
export default Login;

View File

@ -0,0 +1,39 @@
import React from "react";
import { Link } from "react-router-dom";
import { useAuth } from "../../context/useAuth";
interface Props {}
const Navbar = (props: Props) => {
const { isLoggedIn, user, logout } = useAuth();
return (
<nav className="p-6">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-20">
<Link to="/">
<img src="" alt="" />
</Link>
</div>
{isLoggedIn() ? (
<div className="hidden lg:flex items-center space-x-6 text-back">
<div className="hover:text-darkBlue">Welcome, {user?.userName}</div>
<a
onClick={logout}
className="px-8 py-3 font-bold rounded text-white bg-psc hover:opacity-70"
>
Logout
</a>
</div>
) : (
<div className="hidden lg:flex items-center space-x-6 text-back">
<Link to="/login" className="px-8 py-3 font-bold rounded text-white bg-psc hover:opacity-70">
Login
</Link>
</div>
)}
</div>
</nav>
);
};
export default Navbar;

View File

@ -0,0 +1,16 @@
import React from 'reactjs'
import Currency from "../../../helpers/currency"
import {useOrder} from "../../../context/useOrder"
const Calc = () => {
const { getVat, getGross, getNet } = useOrder();
return (
<h3>
<small className="text-body-secondary"> Netto: <Currency price={getNet()} /> + MwSt.: <Currency price={getVat()} /> =</small> Brutto: <Currency price={getGross()} />
</h3>
)
}
export default Calc

View File

@ -0,0 +1,33 @@
import {useParams} from "react-router-dom"
import { useOrder } from "../../context/useOrder"
import { createContext, useState, useEffect } from 'react';
import Calc from "./calc/calc"
import ShippingComponent from "./shipping/shipping"
import PaymentComponent from "./payment/payment"
const Order = () => {
const { loadOrder } = useOrder();
let { uuid } = useParams();
useEffect(() => {
console.log(uuid)
if(uuid) {
loadOrder(uuid)
}
}, [uuid])
return (
<div className="flex flex-row">
<div className="basis-3/4">
<ShippingComponent />
<PaymentComponent />
</div>
<div className="basis-1/4">
<Calc/>
</div>
</div>
)
}
export default Order

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