diff --git a/.eslintrc.js b/.eslintrc.js index 1a6de70734..c29469f887 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,6 +16,7 @@ module.exports = { "Cookies": true, "foodcoopshop": true, "GdprConsent": true, + "math": true, "Quagga": true, "slidebars": true, "Swiper": true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d74e52c89..afadb418fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,60 +7,32 @@ on: pull_request: paths-ignore: - '**.md' - + jobs: ci: runs-on: ubuntu-22.04 - continue-on-error: ${{matrix.experimental}} - strategy: - matrix: - php-version: ['8.1'] - experimental: [false] - services: - mysql: - image: mysql:8.0 - ports: - - 8888:3306 - env: - MYSQL_ROOT_PASSWORD: password - MYSQL_DATABASE: foodcoopshop_test - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - name: PHP ${{matrix.php-version}} - + name: FoodCoopShop CI steps: - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{matrix.php-version}} - env: - update: true #forces minor release update - - - name: Setup NodeJS - uses: actions/setup-node@v2 - with: - node-version: '18' - - - uses: actions/checkout@v2 - - - name: Apply secrets - run: | - sed -i 's/HELLO_CASH_USERNAME/${{secrets.HELLO_CASH_USERNAME}}/' ./.github/workflows/custom_config.php - sed -i 's/HELLO_CASH_PASSWORD/${{secrets.HELLO_CASH_PASSWORD}}/' ./.github/workflows/custom_config.php - sed -i 's/HELLO_CASH_CASHIER_ID/${{secrets.HELLO_CASH_CASHIER_ID}}/' ./.github/workflows/custom_config.php - - - name: Setup DB + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run Docker Compose + run: CURRENT_UID=$(id -u):$(id -g) docker compose up -d + + - name: Setup Dev Environment run: | - mysql --version - mysql -h 127.0.0.1 --port 8888 -u root -ppassword foodcoopshop_test < ./config/sql/_installation/clean-db-structure.sql - mysql -h 127.0.0.1 --port 8888 -u root -ppassword foodcoopshop_test < ./tests/config/sql/test-db-data.sql - - - name: Install vendors + # do not use ./devtools/init-dev-setup.sh because dev db is not needed + bash ./devtools/installation/set-permissions.sh + bash ./devtools/setup-dev/copy-config-files.sh + docker exec -w /app fcs.php composer install + docker exec -w /app/webroot fcs.php npm install + + - name: Apply secrets run: | - composer install --optimize-autoloader --no-progress - cp ./.github/workflows/*.php ./config/ - npm --prefix ./webroot install ./webroot - + sed -i 's/HELLO_CASH_USERNAME/${{secrets.HELLO_CASH_USERNAME}}/' ./config/custom_config.php + sed -i 's/HELLO_CASH_PASSWORD/${{secrets.HELLO_CASH_PASSWORD}}/' ./config/custom_config.php + sed -i 's/HELLO_CASH_CASHIER_ID/${{secrets.HELLO_CASH_CASHIER_ID}}/' ./config/custom_config.php + - name: Upload files to server if: ${{github.event_name == 'push'}} uses: up9cloud/action-rsync@master @@ -75,17 +47,12 @@ jobs: echo ${{secrets.DEPLOY_PATH}}/builds/${{github.ref}} mkdir -p ${{secrets.DEPLOY_PATH}}/builds/${{github.ref}} SSH_ARGS: '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - + - name: PHPStan - if: always() - run: vendor/bin/phpstan analyze -c phpstan.neon --no-interaction --no-progress - + run: docker exec -w /app fcs.php php ./vendor/bin/phpstan analyze -c phpstan.neon --no-interaction --no-progress + - name: ESLint - if: always() - run: | - npm install -g eslint - bash devtools/eslint.sh - - - name: PHPUnit - if: always() - run: vendor/bin/phpunit + run: docker exec -w /app fcs.php bash ./devtools/eslint.sh + + - name: PHPUnit Tests + run: docker exec -w /app fcs.php php ./vendor/bin/phpunit diff --git a/.github/workflows/credentials.php b/.github/workflows/credentials.php deleted file mode 100644 index e8f9f68b4f..0000000000 --- a/.github/workflows/credentials.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -return [ - 'app' => [ - - /** - * set to true if you want to receive debug emails when exceptions are triggered - */ - 'emailErrorLoggingEnabled' => false, - ] -]; diff --git a/.gitignore b/.gitignore index 489cef0c21..6afa81482f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,8 @@ Thumbs.db nbproject/* # Visual Studio Code .vscode +.history/ +*.code-search # Sass preprocessor .sass-cache/ diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000000..6c54dbaa8d --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,26 @@ +tasks: + - name: Bash + - name: Docker + init: | + docker compose pull + CURRENT_UID=$(id -u):$(id -g) docker compose up + +ports: + - port: 8001 + onOpen: notify + - port: 8080 + onOpen: ignore + - port: 8081 + onOpen: ignore + - port: 3320 + onOpen: ignore + - port: 3321 + onOpen: ignore + +github: + prebuilds: + master: false + branches: false + pullRequests: false + pullRequestsFromForks: false + addCheck: false \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b1a2008e64..7bef1c129b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,38 @@ Das Format basiert auf [keepachangelog.com](http://keepachangelog.com) und verwendet [Semantic Versioning](http://semver.org/). +## v3.6.0 + +### Neue Datenschutz-Funktionen +- Personenbezogene Mitglieder-Daten (Vorname, Nachname, E-Mail) können nun für bestimmte Hersteller systemweit **anonymisiert** werden. Im Sinne des Datenschutzes ist das für neu angelegte Hersteller auch die Standard-Einstellung. [I#767](https://github.com/foodcoopshop/foodcoopshop/issues/767) / [I#929](https://github.com/foodcoopshop/foodcoopshop/issues/929) / [PR#930](https://github.com/foodcoopshop/foodcoopshop/pull/930) / [PR#932](https://github.com/foodcoopshop/foodcoopshop/pull/932) +- E-Mail-Adressen, die über den Editor eingegeben wurden (z.B. auf Seiten oder in Blog-Artikeln), werden jetzt automatisch verlinkt und spamgeschützt angezeigt. [I#933](https://github.com/foodcoopshop/foodcoopshop/issues/933) / [PR#943](https://github.com/foodcoopshop/foodcoopshop/pull/934) + +### Neue Funktionen / Verbesserungen +- Ab sofort kann auch ein **dunkles Design / Dark Mode** verwendet werden. Das schont die Augen und spart bei OLED-Bildschirmen auch Strom. Einfach auf den Mond neben dem Anmelde-Link klicken. [I#873](https://github.com/foodcoopshop/foodcoopshop/issues/873) / [PR#913](https://github.com/foodcoopshop/foodcoopshop/pull/913) +- Beim Anpassen des Gewichts für Produkte, die mehrmals bestellt wurden, ist jetzt ein **eingebauter Taschenrechner** hilfreich. Man kann z.B. "192+167" eintippen und das Ergebnis wird automatisch übernommen. Der Taschenrechner ist auch im Selbstbedienungs-Modus integriert. [PR#923](https://github.com/foodcoopshop/foodcoopshop/pull/923) / [Commit](https://github.com/foodcoopshop/foodcoopshop/commit/449aedc29269cd1d74322c3f2239a4953d6500a5) +- Superadmins können Tag und Uhrzeit der **Cronjobs** (z.B. die automatische Bestell-Erinnerung, Rechnungsversand) jetzt selber im Admin-Bereich (Homepage-Verwaltung / Einstellungen / neuer Tab "Cronjobs") ändern. [I#860](https://github.com/foodcoopshop/foodcoopshop/issues/860) / [PR#74](https://github.com/foodcoopshop/foodcoopshop/pull/874) +- Die Überschriften aller Tabellen im Admin-Bereich bleiben jetzt beim Scrollen sichtbar (nicht in iOS). [PR#888](https://github.com/foodcoopshop/foodcoopshop/pull/888) +- Im Produkt-Admin kann jetzt der Status (aktiviert, deaktiviert) von mehreren markierten Produkten auf einmal geändert werden. [I#895](https://github.com/foodcoopshop/foodcoopshop/issues/895) / [PR#897](https://github.com/foodcoopshop/foodcoopshop/pull/897) +- Hersteller können ihre Bestellungen über [eine neue API](https://foodcoopshop.github.io/de/netzwerk-modul.html#6-api-zum-abrufen-von-bestellungen) abrufen und sie so im eigenen System weiterverarbeiten. [I#894](https://github.com/foodcoopshop/foodcoopshop/issues/894) / [PR#899](https://github.com/foodcoopshop/foodcoopshop/pull/899) +- Bei der Umsatzstatistik kann jetzt auch nach "letzte 12 bzw. 24 Monate" gefiltert werden. [I#904](https://github.com/foodcoopshop/foodcoopshop/issues/904) / [PR#908](https://github.com/foodcoopshop/foodcoopshop/pull/908) +- Bei allen Produkten kann jetzt die Anzahl der bestellten Einheiten für den nächsten Abholtag angezeigt werden. Das hilft, wenn bestimmte Gebindegrößen erreicht werden sollen. [I#909](https://github.com/foodcoopshop/foodcoopshop/issues/909) / [PR#910](https://github.com/foodcoopshop/foodcoopshop/pull/910) +- Beim Umbuchen auf ein anderes Mitglied kann jetzt über eine Checkbox ausgewählt werden, ob die betroffenen Mitglieder per Mail benachrichtigt werden sollen. [I#920](https://github.com/foodcoopshop/foodcoopshop/issues/920) / [PR#921](https://github.com/foodcoopshop/foodcoopshop/pull/921) +- Bei einer herstellerbasierten Lieferpause sind Lagerprodukte jetzt weiterhin vorbestellbar. [PR#924](https://github.com/foodcoopshop/foodcoopshop/pull/924) +- Alle Initiativen, die die Funktion "Rechnung an Kunden" aktiviert haben, werden jetzt mit einer E-Mail über einen Bestell-Kommentar benachrichtigt. [PR#926](https://github.com/foodcoopshop/foodcoopshop/pull/926) +- Die Konfiguration "Freitag Bestellschluss / Samstag Bestelllisten-Versand / Donnerstag Abholtag" ist jetzt möglich. [I#866](https://github.com/foodcoopshop/foodcoopshop/issues/866) / [PR#867](https://github.com/foodcoopshop/foodcoopshop/pull/867) + +### For developers +- New 🐳 [Docker Dev Environment](https://foodcoopshop.github.io/en/docker-dev-environment.html) and [Gitpod-Integration](https://gitpod.io/#https://github.com/foodcoopshop/foodcoopshop). [I#871](https://github.com/foodcoopshop/foodcoopshop/issues/871) / [PR#876](https://github.com/foodcoopshop/foodcoopshop/pull/876) / [PR#879](https://github.com/foodcoopshop/foodcoopshop/pull/879) / [PR#881](https://github.com/foodcoopshop/foodcoopshop/pull/881) +- Enable strict typing in every php file to improve code quality. [I#872](https://github.com/foodcoopshop/foodcoopshop/issues/872) / [PR#893](https://github.com/foodcoopshop/foodcoopshop/pull/893) +- Replace CakePHP's deprecated classes: File, Folder, Shell. [I#902](https://github.com/foodcoopshop/foodcoopshop/issues/902) [I#906](https://github.com/foodcoopshop/foodcoopshop/issues/906) / [PR#905](https://github.com/foodcoopshop/foodcoopshop/pull/905) / [PR#907](https://github.com/foodcoopshop/foodcoopshop/pull/907) +- Die Software ist jetzt kompatibel mit PHP 8.2. [I#915](https://github.com/foodcoopshop/foodcoopshop/issues/915) / [PR#916](https://github.com/foodcoopshop/foodcoopshop/pull/916) + +### Bugfixes +- Sofern Rechnungen an Kunden generiert wurden (Dorfladen) UND die Steuer für Pfand nicht auf 20% gesetzt war, wurde die Steuer von geliefertem Pfand trotzdem immer mit 20% berechnet. [I#940](https://github.com/foodcoopshop/foodcoopshop/issues/940) / [PR#941](https://github.com/foodcoopshop/foodcoopshop/pull/941) +- Bei Sofort-Bestellungen ist es ab sofort nicht mehr möglich, das erneute Laden von langsamen Seiten durch wiederholtes Klicken zu erzwingen. Diese Mehrfach-Requests haben nämlich den eingeloggten User und den User, für den bestellt wird, durcheinandergewirbelt. [I#945](https://github.com/foodcoopshop/foodcoopshop/issues/945) / [PR#946](https://github.com/foodcoopshop/foodcoopshop/pull/946) + +Datum: 12.04.2023 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/18) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.5.1...v3.6.0) + # v3.5.1 ### Herzlichen Dank an alle beteiligten Personen @@ -66,239 +98,6 @@ Datum: 28.02.2023 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/ - Neuer CSS-Compressor: CssMin wurde durch CleanCss ersetzt. [PR#856](https://github.com/foodcoopshop/foodcoopshop/pull/856) - Fontawesome v6 Update. [PR#855](https://github.com/foodcoopshop/foodcoopshop/pull/855) -Datum: 12.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/17) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.4.2...v3.5.0) - -# v3.4.2 - -### Security update -Fixed security issue in CKEditor. - -Datum: 18.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/16) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.4.1...v3.4.2) - -# v3.4.1 - -### Bugfix -- Fix error on creating build. [PR#802](https://github.com/foodcoopshop/foodcoopshop/pull/802) - -Datum: 06.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/16) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.4.0...v3.4.1) - -# v3.4.0 - -### Herzlichen Dank an alle beteiligten Personen -* [Confuset](https://github.com/Confuset) -* [mrothauer](https://github.com/mrothauer) -* [ocapito](https://github.com/ocapito) -* [pabneukistl](https://github.com/pabneukistl) - -### Verbesserungen -- Die Summe im Warenkorb ist jetzt in Warenwert, Pfandsumme und Gesamtbetrag aufgesplittet. [I#636](https://github.com/foodcoopshop/foodcoopshop/issues/636) / [PR#699](https://github.com/foodcoopshop/foodcoopshop/pull/699) -- Der Instagram-Account kann nun in den Einstellungen angegeben werden, außerdem ist der Bereich für die Social-Media-Links im Footer neu gestaltet. [I#642](https://github.com/foodcoopshop/foodcoopshop/issues/642) / [PR#706](https://github.com/foodcoopshop/foodcoopshop/pull/706) -- Im Selbstbedienungs-Modus werden die Produkte erst nach Suche, Scannen oder Kategorie-Auswahl angezeigt. Außerdem steht im Kategorien-Dropdown jetzt die Produkt-Anzahl. [I#703](https://github.com/foodcoopshop/foodcoopshop/issues/703) / [PR#704](https://github.com/foodcoopshop/foodcoopshop/pull/704) -- Für Admins und Superadmins ist es jetzt möglich, über den Selbstbedienungs-Modus für andere Mitglieder zu bestellen. Der Vorteil gegenüber der Sofort-Bestellung besteht darin, dass das tatsächlich entnommene Gewicht direkt beim Bestellen eingegeben werden kann. [I#703](https://github.com/foodcoopshop/foodcoopshop/issues/703) / [PR#718](https://github.com/foodcoopshop/foodcoopshop/pull/718) -- Beim Bild-Upload ist jetzt neben dem JPG-Format auch PNG und GIF verwendbar. [I#702](https://github.com/foodcoopshop/foodcoopshop/issues/702) / [PR#720](https://github.com/foodcoopshop/foodcoopshop/pull/720) -- Selbstbedienungs-Modus: Bereits bestehende Barcodes (EAN-13) können nun Produkten und Varianten zugewiesen und zum Scannen verwendet werden. [I#707](https://github.com/foodcoopshop/foodcoopshop/issues/706) / [PR#729](https://github.com/foodcoopshop/foodcoopshop/pull/729) -- Der CSV-Upload für die Guthaben-Aufladungen unterstützt jetzt auch die Volksbank. [I#732](https://github.com/foodcoopshop/foodcoopshop/issues/732) / [PR#733](https://github.com/foodcoopshop/foodcoopshop/pull/733) -- Der zweiwöchige Lieferrhythmus kann jetzt so eingestellt werden, dass die Produkte nur noch in der Woche vor der Lieferung bestellt werden können. In der anderen Woche sind bei aktivierter Einstellung die Produkte beim Bestellen zwar sichtbar, aber nicht bestellbar. Auch bei einem vorgezogenen Bestellschluss, kann nur mehr in der Woche vor der Lieferung bestellt werden (gilt für ein- und zweiwöchig). Alle anderen Lieferrhythmen sind von der neuen Einstellung nicht betroffen. [I#716](https://github.com/foodcoopshop/foodcoopshop/issues/716) / [PR#734](https://github.com/foodcoopshop/foodcoopshop/pull/734) [C](https://github.com/foodcoopshop/foodcoopshop/commit/3e375b05e301c478b6dd0ecd34fdb969e20645bf) [PR#792](https://github.com/foodcoopshop/foodcoopshop/pull/792) [PR#793](https://github.com/foodcoopshop/foodcoopshop/pull/793) -- Im neuen Bereich "Benachrichtigungen" im User-Profil kann der User selbst folgende Benachrichtigungs-E-Mails deaktivieren: Guthaben-Erinnerungs-Mail, Rechnung, Abholtag-Erinnerung, Überweisung wurde ins Guthaben-System übernommen. [I#739](https://github.com/foodcoopshop/foodcoopshop/issues/739) / [I#752](https://github.com/foodcoopshop/foodcoopshop/issues/752) / [PR#740](https://github.com/foodcoopshop/foodcoopshop/pull/740) / [PR#754](https://github.com/foodcoopshop/foodcoopshop/pull/754) -- Als "abgeholt" markierte Bestellungen werden nun im unteren Bereich der Liste "Admin-Bereich / Bestellungen" gruppiert angezeigt. Damit bleibt die Liste auch bei vielen Bestellungen übersichtlich. [C](https://github.com/foodcoopshop/foodcoopshop/commit/cc53502b57b5d42e6ba5f01b2bb9d91383cca253) -- Statusmeldungen (z.B. "Dein Profil wurde erfolgreich geändert.") schließen sich jetzt automatisch nach 5 Sekunden. Fehlermeldungen müssen nach wie vor manuell geschlossen werden. [PR#746](https://github.com/foodcoopshop/foodcoopshop/pull/746) -- Die 🥕 Karotte für neue Mitglieder wird angezeigt, wenn Bestellungen für maximal 2 Abholtage vorhanden sind. [C](https://github.com/foodcoopshop/foodcoopshop/commit/6fe59562a5e96813425a138e27de57d8fb482135) -- Neue Hersteller-Einstellung: "Sollen die Bestelllisten auch Lagerprodukte enthalten?". [I#756](https://github.com/foodcoopshop/foodcoopshop/issues/756) / [PR#758](https://github.com/foodcoopshop/foodcoopshop/pull/758) -- Beim Ändern des Abholtages werden Mitglieder jetzt nicht mehr automatisch per E-Mail benachrichtigt. Die Benachrichtigung kann aber durch eine Checkbox aktiviert werden. [I#787](https://github.com/foodcoopshop/foodcoopshop/issues/787) / [PR#788](https://github.com/foodcoopshop/foodcoopshop/pull/788) - -### Neue Funktionen für den [Einzelhandels-Modus](https://foodcoopshop.github.io/de/dorfladen-online.html) -- Im Selbstbedienungs-Modus wird jetzt nach jedem Einkauf automatisch die Rechnung erstellt. Sie wird auch automatisch gedruckt, wenn die Funktion "Rechnungen per E-Mail erhalten" deaktiviert wurde. [I#696](https://github.com/foodcoopshop/foodcoopshop/issues/696) / [PR#697](https://github.com/foodcoopshop/foodcoopshop/pull/697) -- Die Rechnungen werden nun automatisch gedruckt, bar bezahlt wurde und die Funktion "Rechnungen per E-Mail erhalten" deaktiviert ist. [I#698](https://github.com/foodcoopshop/foodcoopshop/issues/698) / [PR#769](https://github.com/foodcoopshop/foodcoopshop/pull/769) -- Kunden haben jetzt im neuen Menüpunkt "Meine Rechnungen" eine schöne Rechnungs-Übersicht mit Download-Funktion. [I#646](https://github.com/foodcoopshop/foodcoopshop/issues/646) / [PR#705](https://github.com/foodcoopshop/foodcoopshop/pull/705) -- Es gibt nun eine neue Kunden-Einstellung, die Bestellungen zu Einkaufspreisen (für Eigenverbrauch) oder zu Nullpreisen (Vorbestellung von Produkten, die später nochmal verkauft werden) ermöglicht. [I#672](https://github.com/foodcoopshop/foodcoopshop/issues/672) / [PR#712](https://github.com/foodcoopshop/foodcoopshop/pull/712) -- Falls die Funktion zum Erfassen der Einkaufspreise aktiviert ist, können Hersteller nun folgende Daten der eigenen Produkte nicht mehr ändern: Einkaufspreis, Verkaufspreis, Pfand, Steuer. Außerdem wird auf den Bestelllisten der Preis nicht mehr angezeigt. [I#710](https://github.com/foodcoopshop/foodcoopshop/issues/710) [I#711](https://github.com/foodcoopshop/foodcoopshop/issues/711) / [PR#714](https://github.com/foodcoopshop/foodcoopshop/pull/714) [PR#715](https://github.com/foodcoopshop/foodcoopshop/pull/715) -- Weiters werden Produkte bzw. Varianten ohne Einkaufspreis nicht mehr im Produktkatalog angezeigt und können so nicht bestellt werden. [I#745](https://github.com/foodcoopshop/foodcoopshop/issues/745) / [PR#748](https://github.com/foodcoopshop/foodcoopshop/pull/748) -- Die Verkaufspreise können jetzt auch auf Basis eines Aufschlages automatisch berechnet werden. [I#721](https://github.com/foodcoopshop/foodcoopshop/issues/721) / [PR#722](https://github.com/foodcoopshop/foodcoopshop/pull/722) -- Der Aufschlag, also die Differenz zwischen Netto-Einkaufpreis und Netto-Verkaufspreis wird im Admin-Bereich jetzt als Prozent- und Euro-Betrag angezeigt. [I#723](https://github.com/foodcoopshop/foodcoopshop/issues/723) / [PR#736](https://github.com/foodcoopshop/foodcoopshop/pull/736) -- Möglichkeit zum Erstellen eines Lieferscheines für die Hersteller. Die Datei (Excel-Format) enthält alle notwendigen Daten für die Erstellung einer Rechnung an den Betreiber (u.a. die Einkaufspreise). [I#627](https://github.com/foodcoopshop/foodcoopshop/issues/627) / [PR#738](https://github.com/foodcoopshop/foodcoopshop/pull/738) -- Diese Lieferscheine werden ab sofort auch automatisch 1x monatlich (immer am 1.) an die Hersteller versendet. Das Hakerl in den Hersteller-Einstellungen muss dafür aktiviert werden. [I#772](https://github.com/foodcoopshop/foodcoopshop/issues/772) / [PR#789](https://github.com/foodcoopshop/foodcoopshop/pull/789) -- Optimierungen für Barverkäufe. [I#742](https://github.com/foodcoopshop/foodcoopshop/issues/742) / [PR#743](https://github.com/foodcoopshop/foodcoopshop/pull/743) / [PR#747](https://github.com/foodcoopshop/foodcoopshop/pull/747) -- Gutscheine lassen sich nun anhand [dieser Anleitung](https://foodcoopshop.github.io/de/dorfladen-online-gutscheine.html) super im System abbilden. [I#750](https://github.com/foodcoopshop/foodcoopshop/issues/750) / [PR#762](https://github.com/foodcoopshop/foodcoopshop/pull/762) - -### Bugfixes / Updates / Performance -- Bei Installation mit vielen Produkten (> 500) waren Frontend-Seiten mit Produktlisten (Kategorie-Detailseite und Hersteller-Profil) sehr langsam. Jetzt laufen diese deutlich schneller. [I#700](https://github.com/foodcoopshop/foodcoopshop/issues/700) / [PR#764](https://github.com/foodcoopshop/foodcoopshop/pull/764) -- Fehlerhafte SQL-Abfrage mit MariaDB beim Erstellen der Bestelllisten. [I#724](https://github.com/foodcoopshop/foodcoopshop/issues/724) / [PR#726](https://github.com/foodcoopshop/foodcoopshop/pull/726) -- Die Sortierung von Mitgliedern / Herstellern funktioniert jetzt systemweit (z.B. in Admin-Bereich / Bestellungen) auch mit beginnenden Umlauten bzw. Kleinbuchstaben. [C1](5edbc970cc688f7a116ffba04170b391cc5a2e77) / [C2](aa12d4d18d14834dc82a84aa0b8dc068b1356f6d) -- CakePHP Update v4.3. [I#728](https://github.com/foodcoopshop/foodcoopshop/issues/728) / [PR#731](https://github.com/foodcoopshop/foodcoopshop/pull/731) -- BackupDatabaseShell now uses defined port. [PR#749](https://github.com/foodcoopshop/foodcoopshop/pull/749) - -Datum: 06.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/16) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.3.0...v3.4.0) - -# v3.3.0 - -### Herzlichen Dank an alle beteiligten Personen -* [mrothauer](https://github.com/mrothauer) - -### Neue Funktionen -- Pfand-Rückgaben für Mitglieder können nun auch dann eingegeben werden, auch wenn das Mitglied in der aktuellen Woche nicht bestellt hat. Der Button "Pfand-Rückgabe" wird immer angezeigt, das Mitglied kann dann aus einer Dropdown-Liste ausgewählt werden. [I#654](https://github.com/foodcoopshop/foodcoopshop/issues/654) / [PR#655](https://github.com/foodcoopshop/foodcoopshop/pull/655) -- Produkte können jetzt auch einem Lagerort zugewiesen werden (z.B. Keine Kühlung, Kühlschrank, Tiefkühler). In der Liste "Bestellungen als PDF" werden die Produkte entsprechend sortiert angezeigt. [I#662](https://github.com/foodcoopshop/foodcoopshop/issues/662) / [PR#690](https://github.com/foodcoopshop/foodcoopshop/pull/690) -- Das Frontend wurde optisch aufgepeppt: Blog-Artikel, Hauptmenü, das mobile Menü, die Produkt-Liste und der Footer sind nun frischer. Weiters ist die Haupt-Schrift etwas größer und die Fett-Schrift dezenter. [I#643](https://github.com/foodcoopshop/foodcoopshop/issues/643) / [PR#648](https://github.com/foodcoopshop/foodcoopshop/pull/648) - -### Verbesserungen -- Bei der Produktbeschreibung wird jetzt ein Hinweis angezeigt, falls bereits offene Bestellungen vorhanden sind. Irrtümliche doppelte Bestellungen werden so vermieden. [I#681](https://github.com/foodcoopshop/foodcoopshop/issues/681) / [PR#694](https://github.com/foodcoopshop/foodcoopshop/pull/694) -- Bei Produkten ohne Bild wird jetzt das Hersteller-Logo angezeigt. Falls kein Hersteller-Logo vorhanden ist, wird das definierte Standard-Bild anzeigt. [I#490](https://github.com/foodcoopshop/foodcoopshop/issues/490) / [PR#693](https://github.com/foodcoopshop/foodcoopshop/pull/693) -- Beim Erstellen eines neuen Blog-Artikels kann nun angegeben werden, wie lange er auf der Startseite angezeigt werden soll. Danach verschwindet er automatisch. [I#601](https://github.com/foodcoopshop/foodcoopshop/issues/601) / [PR#664](https://github.com/foodcoopshop/foodcoopshop/pull/664) -- Beim Registrieren mit automatischer Aktivierung muss ab sofort die E-Mail-Adresse bestätigt werden, sonst bleibt der neue User inaktiv. [I#656](https://github.com/foodcoopshop/foodcoopshop/issues/656) / [PR#657](https://github.com/foodcoopshop/foodcoopshop/pull/657) -- Das Bestelldatum wird nun im Tooltip über dem Bestellstatus-Icon angezeigt. [I#652](https://github.com/foodcoopshop/foodcoopshop/issues/652) / [PR#653](https://github.com/foodcoopshop/foodcoopshop/pull/653) -- Alle Formulare sind jetzt noch besser gegen potenzielle Angriffe abgesichert. [I#659](https://github.com/foodcoopshop/foodcoopshop/issues/659) / [PR#661](https://github.com/foodcoopshop/foodcoopshop/pull/661) - -### Neue Funktionen für den [Einzelhandels-Modus](https://foodcoopshop.github.io/de/dorfladen-online.html) -- Nahtlose Integration der Registrierkasse HelloCash für den Einzelhandels-Modus. [Zur Online-Doku](https://foodcoopshop.github.io/de/registrierkasse-hello-cash.html). [I#683](https://github.com/foodcoopshop/foodcoopshop/issues/683) / [PR#686](https://github.com/foodcoopshop/foodcoopshop/pull/686) / [I#687](https://github.com/foodcoopshop/foodcoopshop/issues/687) / [PR#691](https://github.com/foodcoopshop/foodcoopshop/pull/691) -- Für Produkte kann nun auch der Einkaufspreis und dessen Umsatzsteuer angegeben werden. Diese Daten werden dann bei den Bestellungen mitgespeichert und liefern so die Datengrundlage für die neue Gewinnermittlung. [I#660](https://github.com/foodcoopshop/foodcoopshop/issues/660) / [PR#680](https://github.com/foodcoopshop/foodcoopshop/pull/680) / [I#671](https://github.com/foodcoopshop/foodcoopshop/issues/671) / [PR#692](https://github.com/foodcoopshop/foodcoopshop/pull/692) - -### Bugfixes -- Guthaben-Aufladungen mit CSV-Upload: Wenn ein Mitglied nicht ausgewählt wurde und man speichern wollte, wurde nicht die Fehlermeldung beim Mitglied angezeigt, sondern die allgemeine Fehlerseite. [I#677](https://github.com/foodcoopshop/foodcoopshop/issues/677) / [PR#678](https://github.com/foodcoopshop/foodcoopshop/pull/678) -- Mehrwöchiger Lieferrhythmus für Produkte mit Standard-Bestellschluss Montag und vorgezogener Bestellschluss Samstag wurde falsch berechnet. [PR#701](https://github.com/foodcoopshop/foodcoopshop/pull/701) - -### Code Cleaning -- Die alte Funktion zum Verwalten der Mitgliedbeiträge wurde entfernt. [Anleitung zum Umstellen auf das neue System](https://foodcoopshop.github.io/de/mitgliedsbeitraege.html). [I#666](https://github.com/foodcoopshop/foodcoopshop/issues/666) / [PR#667](https://github.com/foodcoopshop/foodcoopshop/pull/667) -- Sauberere SQL-Statements durch Verwendung von QueryExpression. [I#644](https://github.com/foodcoopshop/foodcoopshop/issues/644) / [PR#645](https://github.com/foodcoopshop/foodcoopshop/pull/645) - -Datum: 06.09.2021 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/15) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.2.2...v3.3.0) - -# v3.2.2 - -### Security Fix -* Das Registrierungsformular und das Formular zum Bearbeiten des User-Profils sind nun besser abgesichert. - -Datum: 12.04.2021 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.2.1...v3.2.2) - -# v3.2.1 - -### Bugfixes -* Wenn ein Hersteller die eigenen Einstellungen speichert, wird die Ansprechperson jetzt nicht mehr gelöscht. -* Layout-Fix im Overlay für die Produkt-Beschreibung. -* Kamera-Icon wurde auf der Login-Seite für den Selbstbedienung-Modus auf Smartphones nicht angezeigt. -* Wenn die Erstinstallation <= v3.0 war, müssen [zwei Migrations manuell ausgeführt werden](https://foodcoopshop.github.io/en/migration-guide.html). - -Datum: 23.03.2021 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.2.0...v3.2.1) - -# v3.2.0 - -### Herzlichen Dank an alle beteiligten Personen -* [AndreasEgger](https://github.com/AndreasEgger) -* [mantensteiner](https://github.com/mantensteiner) -* [markuskoban](https://github.com/markuskoban) -* [mrothauer](https://github.com/mrothauer) -* [reicharm](https://github.com/reicharm) - -### Neue Funktionen -- Das neue Modul zur Erstellung von Kunden-Rechnungen ermöglicht die Verwendung der Software im Einzelhandel. [Zur Online-Doku](https://foodcoopshop.github.io/de/dorfladen-online.html). [I#572](https://github.com/foodcoopshop/foodcoopshop/issues/572) / [PR#580](https://github.com/foodcoopshop/foodcoopshop/pull/580) / [PR#584](https://github.com/foodcoopshop/foodcoopshop/pull/584) / [PR#599](https://github.com/foodcoopshop/foodcoopshop/pull/599) -- Die Verwaltung der Mitgliedsbeiträge ist nun stark vereinfacht. [Zur Online-Doku](https://foodcoopshop.github.io/de/mitgliedsbeitraege.html). [I#471](https://github.com/foodcoopshop/foodcoopshop/issues/471) / [PR#608](https://github.com/foodcoopshop/foodcoopshop/pull/608) -- 📷 Beim Einkaufen im Selbstbedienungs-Modus kann man nun direkt mit der Smartphone-Kamera (ganz ohne App) die Barcodes scannen. [I#557](https://github.com/foodcoopshop/foodcoopshop/issues/557) / [PR#563](https://github.com/foodcoopshop/foodcoopshop/pull/563) -- Die stark verbesserte Pfand-Übersicht bringt endlich Licht 💡 in den Pfand-Dschungel 🐵, der sich bei manchen Initiativen über die Jahre ergeben hat. [I#570](https://github.com/foodcoopshop/foodcoopshop/issues/570) / [PR#571](https://github.com/foodcoopshop/foodcoopshop/pull/571) -- ☑ Beim Kontrollieren der Bestellungen bleiben die Produkte jetzt angehakt, wenn z.B. Gewicht oder Preis geändert wird. Wenn die Hakerl nicht wieder entfernt werden, sind sie nach 24 Stunden automatisch weg. [I#616](https://github.com/foodcoopshop/foodcoopshop/issues/616) / [PR#617](https://github.com/foodcoopshop/foodcoopshop/pull/617) -- Das eingestellte Guthaben-Limit kann nun beim normalen Bestellen nicht mehr unterschritten werden. Bei Sofort-Bestellungen und Preis- bzw. Gewichtsanpassungen ist dies aber weiterhin möglich. [I#555](https://github.com/foodcoopshop/foodcoopshop/issues/555) / [PR#574](https://github.com/foodcoopshop/foodcoopshop/pull/574) / [PR#603](https://github.com/foodcoopshop/foodcoopshop/pull/603) / [PR#635](https://github.com/foodcoopshop/foodcoopshop/pull/635) -- Die Guthaben-Höhe, ab der die Guthaben-Erinnerungsmail versendet wird, kann nun individuell eingestellt werden. Eine Erhöhung auf z.B. 50 € ist für Initiativen sinnvoll, die den CSV-Upload verwenden. [I#621](https://github.com/foodcoopshop/foodcoopshop/issues/621) / [PR#622](https://github.com/foodcoopshop/foodcoopshop/pull/622) -- Das individuelle Farbschema wird jetzt auch im Admin-Bereich angewendet. [I#613](https://github.com/foodcoopshop/foodcoopshop/issues/613) / [PR#630](https://github.com/foodcoopshop/foodcoopshop/pull/630) - -### Kleinere Verbesserungen -- Es gibt neue Lieferrhythmen: "jeder 2., 3. bzw. 4. Freitag im Monat" [I#581](https://github.com/foodcoopshop/foodcoopshop/issues/581) / [PR#582](https://github.com/foodcoopshop/foodcoopshop/pull/582) / Validierung: [PR#624](https://github.com/foodcoopshop/foodcoopshop/pull/624) -- Slideshow-Bilder auf der Startseite können jetzt verlinkt und außerdem auch "nur für Mitglieder" angezeigt werden. [I#600](https://github.com/foodcoopshop/foodcoopshop/issues/600) / [PR#606](https://github.com/foodcoopshop/foodcoopshop/pull/606) -- Die automatisch versendeten E-Mails bei Preis- und Gewichtsanpassungen von bestellten Produkten können nun global abgestellt werden. [I#576](https://github.com/foodcoopshop/foodcoopshop/issues/576) / [PR#577](https://github.com/foodcoopshop/foodcoopshop/pull/577) -- Fehlerhafte Gewichtsänderungen (z.B. 700 kg statt 700 g) können nun nicht mehr getätigt werden. [I#590](https://github.com/foodcoopshop/foodcoopshop/issues/590) / [PR#593](https://github.com/foodcoopshop/foodcoopshop/pull/593) -- Beim Ändern des Abgeholt-Status wird nun überprüft, ob das Gewicht für alle Produkte eingetragen wurde. [I#614](https://github.com/foodcoopshop/foodcoopshop/issues/614) / [PR#615](https://github.com/foodcoopshop/foodcoopshop/pull/615) -- Zur besseren Übersicht wird das "Bestellbar bis"-Datum jetzt bei jedem Produkt angezeigt. Außer bei Produkten mit wöchentlichem Lieferrhythmus und Standard-Bestellschluss. [I#585](https://github.com/foodcoopshop/foodcoopshop/issues/585) / [PR#594](https://github.com/foodcoopshop/foodcoopshop/pull/594) -- Produktbilder im Hochformat werden jetzt in der Lightbox besser dargestellt. [I#579](https://github.com/foodcoopshop/foodcoopshop/issues/579) / [PR#596](https://github.com/foodcoopshop/foodcoopshop/pull/596) -- Möglichkeit zur kompletten Deaktivierung des Pfand-Systems. [I#604](https://github.com/foodcoopshop/foodcoopshop/issues/604) / [PR#607](https://github.com/foodcoopshop/foodcoopshop/pull/607) -- Es wird nun ein Cookie-Banner eingeblendet und die Datenschutzerklärung ist wieder aktuell bezüglich der verwendeten Cookies. [I#619](https://github.com/foodcoopshop/foodcoopshop/issues/619) / [PR#620](https://github.com/foodcoopshop/foodcoopshop/pull/620) -- Auf den Produkt-Bestelllisten scheint nun auch die Summe der Produkt-Einheit (kg, g) auf. [I#333](https://github.com/foodcoopshop/foodcoopshop/issues/333) / [PR#578](https://github.com/foodcoopshop/foodcoopshop/pull/578) -- Die Software ist kompatibel mit PHP 8.0. -- Das Logo kann jetzt auch in anderen Formaten verwendet werden. Neuer Standard ist PNG. [PR#637](https://github.com/foodcoopshop/foodcoopshop/pull/637) - -### Bugfixes -- Bestelllisten- und Rechnungsversand werden jetzt über eine Queue versendet. Das verhindert seltene, aber nervige Fehler beim Versenden. [I#410](https://github.com/foodcoopshop/foodcoopshop/issues/410) / [I#560](https://github.com/foodcoopshop/foodcoopshop/issues/560) / [I#562](https://github.com/foodcoopshop/foodcoopshop/issues/562) / [PR#561](https://github.com/foodcoopshop/foodcoopshop/pull/561) / [PR#566](https://github.com/foodcoopshop/foodcoopshop/pull/566) / [PR#553](https://github.com/foodcoopshop/foodcoopshop/pull/553) -- Hochgeladene Bilder (z.B. Slideshow) waren machmal leicht unscharf. [I#558](https://github.com/foodcoopshop/foodcoopshop/issues/558) / [PR#573](https://github.com/foodcoopshop/foodcoopshop/pull/573) -- Deutsche IBANs können nun eingetragen werden, das Feld war zu kurz. [I#559](https://github.com/foodcoopshop/foodcoopshop/issues/559) / [PR#564](https://github.com/foodcoopshop/foodcoopshop/pull/564) -- Lieferrhythmus "erster Freitag im Monat" kombiniert mit "Sonntag Bestellschluss" hat nicht korrekt funktioniert. [I#567](https://github.com/foodcoopshop/foodcoopshop/issues/567) / [PR#568](https://github.com/foodcoopshop/foodcoopshop/pull/568) -- Monatlicher Lieferrhythmus kombiniert mit "erster Liefertag" hat nicht korrekt funktioniert. [I#623](https://github.com/foodcoopshop/foodcoopshop/issues/623) / [PR#624](https://github.com/foodcoopshop/foodcoopshop/pull/624) - -### Code Cleaning -- Die Übersetzung auf Polnisch wurde entfernt. Sie wurde nicht mehr verwendet und auch nicht mehr upgedatet. [I#631](https://github.com/foodcoopshop/foodcoopshop/issues/631) / [PR#632](https://github.com/foodcoopshop/foodcoopshop/pull/632) -- CI-Umstellung von Travis auf Github Actions [PR#556](https://github.com/foodcoopshop/foodcoopshop/pull/556) - -### Deprecations -- ⚠️⚠️⚠️ Wer das [Stundenabrechnungs-Modul](https://foodcoopshop.github.io/de/stundenabrechnungs-modul.html) aktiv verwendet, soll sich bitte bei mir melden. Ich habe nämlich den Eindruck, dass es kaum in Gebrauch ist. Da aber die Wartung Aufwand bedeutet, werde ich das Modul - sofern sich bis dahin niemand meldet - ab v3.3 (Herbst 2021) aus der Software entfernen. - -Datum: 08.03.2021 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/14) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.1.0...v3.2.0) - -# v3.1.0 - -### Herzlichen Dank an alle beteiligten Personen -* [AndreasEgger](https://github.com/AndreasEgger) -* [dpakach](https://github.com/dpakach) -* [mrothauer](https://github.com/mrothauer) -* [swoichha](https://github.com/swoichha) -* [vmvbruck](https://github.com/vmvbruck) - -### Neue Funktionen -- Automatischer Kontoabgleich für das Guthaben-System (CSV-Upload). [Zur Online-Doku](https://foodcoopshop.github.io/de/guthaben-system-mit-automatischem-kontoabgleich). [I#463](https://github.com/foodcoopshop/foodcoopshop/issues/463) / [PR#474](https://github.com/foodcoopshop/foodcoopshop/pull/474) -- Mitglieder können nun Feedback zu Produkten abgeben, der Hersteller wird automatisch per E-Mail darüber informiert. [I#391](https://github.com/foodcoopshop/foodcoopshop/issues/391) / [PR#536](https://github.com/foodcoopshop/foodcoopshop/pull/536) -- Viele Overlays (z.B. "Gewicht ändern", "Bild-Upload", "Abmelden") sind nun benutzerfreundlicher und systemweit vereinheitlicht. [I#328](https://github.com/foodcoopshop/foodcoopshop/issues/328) / [PR#524](https://github.com/foodcoopshop/foodcoopshop/pull/524) / [PR#530](https://github.com/foodcoopshop/foodcoopshop/pull/530) / [PR#537](https://github.com/foodcoopshop/foodcoopshop/pull/537) / [PR#538](https://github.com/foodcoopshop/foodcoopshop/pull/538) -- Verbesserungen bei der Gewichtsanpassung: Auch gleiches Gewicht ist nach dem Speichern nicht mehr rot hinterlegt. / Bei bereits verrechneten Bestellungen wird das Gewicht niemals rot angezeigt. / Neues Gewicht ist in der E-Mail-Betreffzeile - damit Fehler wie z.B. 540 kg (statt g) schneller auffallen. / Kein E-Mail-Versand falls das Gewicht gleich bleibt. [I#423](https://github.com/foodcoopshop/foodcoopshop/issues/423) / [PR#479](https://github.com/foodcoopshop/foodcoopshop/pull/479) -- Es ist jetzt möglich, als Bestellschluss für bestimmte Produkte auch **zwei Tage** vor dem Standard-Bestellschluss auszuwählen. Bisher war das nur für den Vortag möglich. [I#487](https://github.com/foodcoopshop/foodcoopshop/issues/487) / [PR#489](https://github.com/foodcoopshop/foodcoopshop/pull/489) -- 😍 Ab sofort können Emojis verwendet werden: Z.B. in Blog-Artikeln, Seiten oder beim Stornieren. Im Editor gibt's dazu ein neues Icon, beim Tippen von einem Doppelpunkt und zwei Buchstaben wird automatisch eine Liste mit Emojis angezeigt. [I#464](https://github.com/foodcoopshop/foodcoopshop/issues/464) / [PR#478](https://github.com/foodcoopshop/foodcoopshop/pull/478) -- Falls Produkte auch für uneingeloggte Mitglieder angezeigt werden, wird nun auch der In-den-Warenkorb-Button angezeigt. Wenn man darauf klickt, erhält man die Meldung, dass man sich zuerst registrieren muss. [I#499](https://github.com/foodcoopshop/foodcoopshop/issues/499) / [PR#500](https://github.com/foodcoopshop/foodcoopshop/pull/500) -- Neue Produkte werden nun auch auf der Startseite angezeigt. Das kann in den Einstellungen ausgestellt werden. [I#504](https://github.com/foodcoopshop/foodcoopshop/issues/504) / [PR#506](https://github.com/foodcoopshop/foodcoopshop/pull/506) -- Kunden von Hofläden können den Abholtag selbst beim Bestellabschluss auswählen. [Zur Online-Doku](https://foodcoopshop.github.io/de/hofladen-online.html). [PR#542](https://github.com/foodcoopshop/foodcoopshop/pull/542) - -### Bugfixes / Optimierungen -- Beim Ändern der Anzahl bzw. Anzahl-Einstellungen von Produkten wird der alte Wert nun wieder unter Aktivitäten angezeigt. [I#514](https://github.com/foodcoopshop/foodcoopshop/issues/514) / [PR#515](https://github.com/foodcoopshop/foodcoopshop/pull/515) -- Horizontales Scrollen auf kleinen Bildschirmen hat das Layout zerschossen. [I#497](https://github.com/foodcoopshop/foodcoopshop/issues/497) / [PR#498](https://github.com/foodcoopshop/foodcoopshop/pull/498) -- Man bleibt jetzt 30 Tage lang angemeldet, wenn man die Funkion "Angemeldet bleiben" verwendet. Bisher waren es 6. [I#492](https://github.com/foodcoopshop/foodcoopshop/issues/492) / [PR#493](https://github.com/foodcoopshop/foodcoopshop/pull/493) -- Die Daten für die Mitglieder-Drodowns im Admin-Bereich werden nun erst nach dem Daraufklicken geladen. Das lädt die Seiten schneller, besonders bei Initativen mit vielen Mitgliedern. [I#477](https://github.com/foodcoopshop/foodcoopshop/issues/477) / [PR#501](https://github.com/foodcoopshop/foodcoopshop/pull/501) -- Die automatische Zeichenbeschränkung in Formularen hat nicht mehr funktioniert (z.B. Feld "Kammer", Feld "Vorname") und führte zu einem Datenbank-Fehler. [I#485](https://github.com/foodcoopshop/foodcoopshop/issues/485) / [I#521](https://github.com/foodcoopshop/foodcoopshop/issues/521) / [PR#488](https://github.com/foodcoopshop/foodcoopshop/pull/525) / [PR#525](https://github.com/foodcoopshop/foodcoopshop/pull/488) -- Infotext bei der Lieferpause ist jetzt leichter verständlich. [I#469](https://github.com/foodcoopshop/foodcoopshop/issues/469) / [PR#482](https://github.com/foodcoopshop/foodcoopshop/pull/482) -- Bestelllisten sind ab und zu nicht über die Fallback-Konfiguration versendet worden. [I#495](https://github.com/foodcoopshop/foodcoopshop/issues/495) / [PR#496](https://github.com/foodcoopshop/foodcoopshop/pull/496) -- Der neue PDF-Writer kann nun PDFs unabhängig von Controllern erzeugen (als Attachment, Inline oder File). [I#412](https://github.com/foodcoopshop/foodcoopshop/issues/412) / [PR#508](https://github.com/foodcoopshop/foodcoopshop/pull/508) -- Bei der Validierung der E-Mail-Adressen wird jetzt auch der MX-Eintrag überprüft. Das vermeidet das Eintragen von ungültigen E-Mail-Adressen, die zwar syntaktisch korrekt sind, bei denen sich aber beim Domainnamen ein Tippfehler eingeschlichen hat. [I#465](https://github.com/foodcoopshop/foodcoopshop/issues/465) / [PR#516](https://github.com/foodcoopshop/foodcoopshop/pull/516) -- Home- und Blog-Slider: OwlCarousel2 wurde ersetzt mit Swiper. [I#512](https://github.com/foodcoopshop/foodcoopshop/issues/512) / [PR#535](https://github.com/foodcoopshop/foodcoopshop/pull/535) -- All tests now work without HttpClient and use IntegrationTestTrait, the tests are now about 45% faster! [I#404](https://github.com/foodcoopshop/foodcoopshop/issues/404) / [PR#550](https://github.com/foodcoopshop/foodcoopshop/pull/550) / [PR#529](https://github.com/foodcoopshop/foodcoopshop/pull/529) / [PR#531](https://github.com/foodcoopshop/foodcoopshop/pull/531) / [PR#532](https://github.com/foodcoopshop/foodcoopshop/pull/532) -- FoodCoopShop verwendet jetzt CakePHP v4.1.x. [I#541](https://github.com/foodcoopshop/foodcoopshop/issues/541) / [PR#545](https://github.com/foodcoopshop/foodcoopshop/pull/545) - -Datum: 07.09.2020 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/13) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.0.2...v3.1.0) - -# v3.0.2 - -### Bugfix -- Produkte waren fehlerhafterweise bestellbar, wenn das Produkt früher mal als Lagerprodukt deklariert war und das Feld "Bestellbar bis zu einer Anzahl von" einen Wert < 0 enthielt. -- Es gab immer wieder Probleme beim automatischen Vermindern der Anzahl, wenn im gleichen Warenkorb ein Produkt mit einer Variante vorhanden war und dieses Produkt genau vor dem entsprechenden Produkt gereiht war. War schwer zu finden... / [PR#484](https://github.com/foodcoopshop/foodcoopshop/pull/484) - -Datum: 26.03.2020 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.0.1...v3.0.2) - -# v3.0.1 - -### Bugfix -- Kategorien wurden nicht korrekt sortiert. / [C](https://github.com/foodcoopshop/foodcoopshop/commit/35d940d82912200d6aab60dd6adc5fedbb68b4de) - -Datum: 22.03.2020 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.0.0...v3.0.1) - -# v3.0.0 - -### Herzlichen Dank an alle beteiligten Personen -* [AndreasEgger](https://github.com/AndreasEgger) -* [mrothauer](https://github.com/mrothauer) - -### Neue Funktionen -- Bei Produkten kann nun als Anzahl "immer verfügbar" eingestellt werden. Weiters kann mittels "Standard-Anzahl pro Lieferrhythmus" festgelegt werden, auf welche verfügbare Anzahl nach erfolgtem Bestelllisten-Versand automatisch wieder hochgezählt wird. Details in der [Online-Dokumentation](https://foodcoopshop.github.io/de/produkte). [I#452](https://github.com/foodcoopshop/foodcoopshop/issues/452) / [I#324](https://github.com/foodcoopshop/foodcoopshop/issues/324) / [PR#457](https://github.com/foodcoopshop/foodcoopshop/pull/457) / -- Das Hauptmenü des Frontends wird nun eingeblendet, sobald man nach oben scrollt. [I#438](https://github.com/foodcoopshop/foodcoopshop/issues/438) / [PR#440](https://github.com/foodcoopshop/foodcoopshop/pull/440) -- Produkte von Sammelbestellungen, bei denen die Bestellfrist bereits erreicht wurde, können über die Sofort-Bestellung jetzt trotzdem bestellt werden. Das ist praktisch für Nachbuchungen. [I#443](https://github.com/foodcoopshop/foodcoopshop/issues/454) / [PR#454](https://github.com/foodcoopshop/foodcoopshop/pull/440) - -### Bugfixes / Updates -- Die Auto-Login-Funktion ("Angemeldet bleiben") hat nicht mehr richtig funktioniert. [I#439](https://github.com/foodcoopshop/foodcoopshop/issues/439) / [PR#444](https://github.com/foodcoopshop/foodcoopshop/pull/444) -- Beim Löschen eines Mitgliedes werden die Bestellungen nun auf "mit dem Hersteller verrechnet" überprüft (und nicht mehr, ob sie 2 Monate alt sind). Weiters wird überprüft, ob die Guthaben-Aufladungen der letzten zwei Jahre bestätigt sind. [I#451](https://github.com/foodcoopshop/foodcoopshop/issues/451) / [PR#456](https://github.com/foodcoopshop/foodcoopshop/pull/456) -- Unter "Aktivitäten" wird ab sofort bei Einträgen des Rechnungsversands die korrekte Uhrzeit angezeigt. Diese war bisher auf 00:00 gesetzt. [I#451](https://github.com/foodcoopshop/foodcoopshop/issues/451) / [PR#455](https://github.com/foodcoopshop/foodcoopshop/pull/455) -- FoodCoopShop verwendet jetzt [CakePHP v4.0](https://book.cakephp.org/4/en/index.html). [I#445](https://github.com/foodcoopshop/foodcoopshop/issues/445) / [PR#446](https://github.com/foodcoopshop/foodcoopshop/pull/446) -- FoodCoopShop ist jetzt mit PHP 7.4 kompatibel. [I#448](https://github.com/foodcoopshop/foodcoopshop/issues/448) / [PR#449](https://github.com/foodcoopshop/foodcoopshop/pull/449) -- Die Begrenzung der maximalen Zeichenanzahl bei Seiten, Blog-Artikel, Produkt- und Herstellerbeschreibung wurde erhöht. [I#460](https://github.com/foodcoopshop/foodcoopshop/issues/460) / [PR#462](https://github.com/foodcoopshop/foodcoopshop/pull/462) -- Neue Kategorien waren nicht mehr alfabetisch sortiert. [I#458](https://github.com/foodcoopshop/foodcoopshop/issues/458) / [PR#459](https://github.com/foodcoopshop/foodcoopshop/pull/459) -- Legacy-Code von FCS v2 wurde entfernt. [PR#468](https://github.com/foodcoopshop/foodcoopshop/pull/468) - -Datum: 20.03.2020 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/12) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v2.7.1...3.0.0) +Datum: 12.09.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/17) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.4.2...v3.5.0) -[Zum Changelog von FoodCoopShop v2.x](devtools/CHANGELOG-v2.md) +[Zum Changelog von FoodCoopShop v3.0-v3.4](devtools/CHANGELOG-v3.md) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..3dd9f0cf91 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM node:18-alpine AS node +FROM webdevops/php-nginx:8.2-alpine + +#https://stackoverflow.com/questions/44447821/how-to-create-a-docker-image-for-php-and-node +COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules +COPY --from=node /usr/local/bin/node /usr/local/bin/node + +RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm + +RUN apk update && \ + apk add gettext && \ + npm install -g npm-check-updates && \ + npm install -g eslint && \ + npm install -g npm@9.6.2 + +#avoid permission error on gitpod on running npm install +RUN npm config set cache /app/tmp --global diff --git a/README.md b/README.md index b42700bfc2..c0c5259152 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Latest stable version - Build status + Build status + @@ -26,15 +26,23 @@ · Docs · - Crowdfunding - · German Demo · English Demo · Users + · + Signal Support Group +## 🤖 Self-hosting / developing +* 🐳 [Docker Dev Environment](https://foodcoopshop.github.io/en/docker-dev-environment) / [Installation guide](https://foodcoopshop.github.io/en/installation-guide) + +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/foodcoopshop/foodcoopshop) +* Gitpod: When all containers are up and running (takes about 1 minute), open your Bash-terminal (not in the Docker-terminal) and run +* `bash ./devtools/init-dev-setup.sh` +* In your custom_config.php, change `App.fullBaseUrl` to your Gitpod-Domain (BE AWARE: NO TRAILING SLASH!). + ## ✨ Features * user-friendly web shop optimized for selling food from different producers * many delivery rhythms for products (once a week, every first / last friday...) @@ -48,21 +56,17 @@ ## ✔ Requirements * Server with **shell access** and **cronjobs** * Apache with `mod_rewrite` -* PHP >= 8.1 +* PHP >= 8.2 * PHP intl extension INTL_ICU_VERSION >= 50.1 -* PHP ZipArchive class +* PHP bzip2 lib (for automatic database backups) * MySQL >= 8.0 * Node.js and npm >= v7 ([installation](https://www.npmjs.com/get-npm)) developer packages * Composer v2 ([installation](https://getcomposer.org/download/)) developer packages * Basic understanding of Apache Webserver, MySQL Database and Linux Server administration -* PHP needs to be able to call mysqldump with exec() for database backups ## ❗ Legal information Before installing don't forget to read the legal information in [German](https://foodcoopshop.github.io/de/rechtliches) or [English](https://foodcoopshop.github.io/en/legal-information). -## 🤖 Self-hosting / developing -Our [installation guide](https://foodcoopshop.github.io/en/installation-guide) helps you. - ## 😎 Maintainer [Mario Rothauer](https://github.com/mrothauer) started the project in 2014 and maintains it. diff --git a/VERSION.txt b/VERSION.txt index 3c8ff8c36b..084e244cea 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -3.5.1 \ No newline at end of file +3.6.0 \ No newline at end of file diff --git a/composer.json b/composer.json index b02d357402..59f6d8482e 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "config": { "platform": { - "php": "8.1" + "php": "8.2" }, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, @@ -19,7 +19,7 @@ } }, "require": { - "php": ">=8.1", + "php": ">=8.2", "cakephp/cakephp": "4.4.*", "cakephp/migrations": "^3.0", "cakephp/plugin-installer": "^1.0", @@ -27,19 +27,21 @@ "intervention/image": "2.*", "cviebrock/discourse-php": "^0.9.3", "studio-42/elfinder": "^2.1", - "ezyang/htmlpurifier": "^4.11", + "ezyang/htmlpurifier": "^4.16", "league/csv": "^9.5", "dereuromark/cakephp-queue": "^6.0", "tecnickcom/tcpdf": "^6.4", "hisorange/browser-detect": "^4.4", - "phpoffice/phpspreadsheet": "1.*" + "phpoffice/phpspreadsheet": "1.*", + "druidfi/mysqldump-php": "^1.0" }, "require-dev": { - "psy/psysh": "@stable", "cakephp/debug_kit": "^4.0", "cakephp/cakephp-codesniffer": "^4.0", "phpunit/phpunit": "^9.0", - "phpstan/phpstan": "^1.0" + "phpstan/phpstan": "^1.0", + "cakephp/bake": "^2.8", + "ergebnis/phpstan-rules": "^1.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index efc9f77d47..de989855ac 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,69 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "17cb42bd846c8e8ca416c3cd2f8e5915", + "content-hash": "006b3e76d46365ca3fa67a931801ea0d", "packages": [ + { + "name": "brick/varexporter", + "version": "0.3.8", + "source": { + "type": "git", + "url": "https://github.com/brick/varexporter.git", + "reference": "b5853edea6204ff8fa10633c3a4cccc4058410ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/varexporter/zipball/b5853edea6204ff8fa10633c3a4cccc4058410ed", + "reference": "b5853edea6204ff8fa10633c3a4cccc4058410ed", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^8.5 || ^9.0", + "vimeo/psalm": "4.23.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\VarExporter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", + "keywords": [ + "var_export" + ], + "support": { + "issues": "https://github.com/brick/varexporter/issues", + "source": "https://github.com/brick/varexporter/tree/0.3.8" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-01-21T23:05:38+00:00" + }, { "name": "cakephp/cakephp", - "version": "4.4.5", + "version": "4.4.12", "source": { "type": "git", "url": "https://github.com/cakephp/cakephp.git", - "reference": "45c91d7ed8a8804d20daf2e001e8e2b292aed77d" + "reference": "ecacba23d96a5e5052716bf19fe8b96c5549acf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/45c91d7ed8a8804d20daf2e001e8e2b292aed77d", - "reference": "45c91d7ed8a8804d20daf2e001e8e2b292aed77d", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/ecacba23d96a5e5052716bf19fe8b96c5549acf3", + "reference": "ecacba23d96a5e5052716bf19fe8b96c5549acf3", "shasum": "" }, "require": { @@ -37,6 +86,14 @@ "psr/log": "^1.0 || ^2.0", "psr/simple-cache": "^1.0 || ^2.0" }, + "provide": { + "psr/container-implementation": "^1.0 || ^2.0", + "psr/http-client-implementation": "^1.0", + "psr/http-server-handler-implementation": "^1.0", + "psr/http-server-middleware-implementation": "^1.0", + "psr/log-implementation": "^1.0 || ^2.0", + "psr/simple-cache-implementation": "^1.0 || ^2.0" + }, "replace": { "cakephp/cache": "self.version", "cakephp/collection": "self.version", @@ -108,20 +165,20 @@ "issues": "https://github.com/cakephp/cakephp/issues", "source": "https://github.com/cakephp/cakephp" }, - "time": "2022-08-29T00:25:46+00:00" + "time": "2023-03-24T01:27:09+00:00" }, { "name": "cakephp/chronos", - "version": "2.3.0", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/cakephp/chronos.git", - "reference": "3ecd6e7ae191c676570cd1bed51fd561de4606dd" + "reference": "a21b7b633f483c4cf525d200219d200f551ee38b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/3ecd6e7ae191c676570cd1bed51fd561de4606dd", - "reference": "3ecd6e7ae191c676570cd1bed51fd561de4606dd", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/a21b7b633f483c4cf525d200219d200f551ee38b", + "reference": "a21b7b633f483c4cf525d200219d200f551ee38b", "shasum": "" }, "require": { @@ -152,48 +209,47 @@ }, { "name": "The CakePHP Team", - "homepage": "http://cakephp.org" + "homepage": "https://cakephp.org" } ], "description": "A simple API extension for DateTime.", - "homepage": "http://cakephp.org", + "homepage": "https://cakephp.org", "keywords": [ "date", "datetime", "time" ], "support": { - "irc": "irc://irc.freenode.org/cakephp", "issues": "https://github.com/cakephp/chronos/issues", "source": "https://github.com/cakephp/chronos" }, - "time": "2021-10-17T02:44:05+00:00" + "time": "2022-11-08T02:17:04+00:00" }, { "name": "cakephp/migrations", - "version": "3.5.2", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/cakephp/migrations.git", - "reference": "e1b03bfef53ce41feabbf2120021ad5187e80289" + "reference": "8eaf51d88fc4350a66fd98118828f9a75ff1e0d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/migrations/zipball/e1b03bfef53ce41feabbf2120021ad5187e80289", - "reference": "e1b03bfef53ce41feabbf2120021ad5187e80289", + "url": "https://api.github.com/repos/cakephp/migrations/zipball/8eaf51d88fc4350a66fd98118828f9a75ff1e0d2", + "reference": "8eaf51d88fc4350a66fd98118828f9a75ff1e0d2", "shasum": "" }, "require": { "cakephp/cache": "^4.3.0", "cakephp/orm": "^4.3.0", - "php": ">=7.2.0", - "robmorgan/phinx": "^0.12" + "php": ">=7.4.0", + "robmorgan/phinx": "^0.13.2" }, "require-dev": { "cakephp/bake": "^2.6.0", "cakephp/cakephp": "^4.3.0", "cakephp/cakephp-codesniffer": "^4.1", - "phpunit/phpunit": "^8.5.0 || ^9.5.0" + "phpunit/phpunit": "^9.5.0" }, "suggest": { "cakephp/bake": "If you want to generate migrations.", @@ -227,7 +283,7 @@ "issues": "https://github.com/cakephp/migrations/issues", "source": "https://github.com/cakephp/migrations" }, - "time": "2022-05-10T15:01:58+00:00" + "time": "2023-03-22T18:19:12+00:00" }, { "name": "cakephp/plugin-installer", @@ -280,16 +336,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.3.3", + "version": "1.3.5", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c" + "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/30897edbfb15e784fe55587b4f73ceefd3c4d98c", - "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/74780ccf8c19d6acb8d65c5f39cd72110e132bbd", + "reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd", "shasum": "" }, "require": { @@ -336,7 +392,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.3.3" + "source": "https://github.com/composer/ca-bundle/tree/1.3.5" }, "funding": [ { @@ -352,7 +408,7 @@ "type": "tidelift" } ], - "time": "2022-07-20T07:14:26+00:00" + "time": "2023-01-11T08:27:00+00:00" }, { "name": "cviebrock/discourse-php", @@ -405,19 +461,20 @@ }, { "name": "dereuromark/cakephp-queue", - "version": "6.4.3", + "version": "6.6.2", "source": { "type": "git", "url": "https://github.com/dereuromark/cakephp-queue.git", - "reference": "d16535e928bfb12aa96458e14f1c4ab5b2f846da" + "reference": "bbd339a771ae753afa40cf78f0bdf2c5546069f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dereuromark/cakephp-queue/zipball/d16535e928bfb12aa96458e14f1c4ab5b2f846da", - "reference": "d16535e928bfb12aa96458e14f1c4ab5b2f846da", + "url": "https://api.github.com/repos/dereuromark/cakephp-queue/zipball/bbd339a771ae753afa40cf78f0bdf2c5546069f5", + "reference": "bbd339a771ae753afa40cf78f0bdf2c5546069f5", "shasum": "" }, "require": { + "brick/varexporter": "^0.3.5", "cakephp/cakephp": "^4.3.0", "php": ">=7.4" }, @@ -472,24 +529,98 @@ "issues": "https://github.com/dereuromark/cakephp-queue/issues", "source": "https://github.com/dereuromark/cakephp-queue" }, - "time": "2022-08-19T11:35:33+00:00" + "time": "2023-03-27T14:30:32+00:00" + }, + { + "name": "druidfi/mysqldump-php", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/druidfi/mysqldump-php.git", + "reference": "15da1dd26e638674bc73fd963b33b26282c14c96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/druidfi/mysqldump-php/zipball/15da1dd26e638674bc73fd963b33b26282c14c96", + "reference": "15da1dd26e638674bc73fd963b33b26282c14c96", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "ext-pdo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.15 || ^9", + "squizlabs/php_codesniffer": "3.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Druidfi\\Mysqldump\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "Druid.fi", + "email": "info@druid.fi" + }, + { + "name": "Diego Torres", + "homepage": "https://github.com/ifsnop", + "role": "Developer" + } + ], + "description": "PHP version of mysqldump cli that comes with MySQL", + "homepage": "https://github.com/druidfi/mysqldump-php", + "keywords": [ + "PHP7", + "database", + "mariadb", + "mysql", + "mysql-backup", + "mysqldump", + "pdo", + "php", + "php8", + "sql" + ], + "support": { + "issues": "https://github.com/druidfi/mysqldump-php/issues", + "source": "https://github.com/druidfi/mysqldump-php/tree/1.0.2" + }, + "time": "2022-12-31T09:51:45+00:00" }, { "name": "ezyang/htmlpurifier", - "version": "v4.14.0", + "version": "v4.16.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "12ab42bd6e742c70c0a52f7b82477fcd44e64b75" + "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/12ab42bd6e742c70c0a52f7b82477fcd44e64b75", - "reference": "12ab42bd6e742c70c0a52f7b82477fcd44e64b75", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8", + "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8", "shasum": "" }, "require": { - "php": ">=5.2" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" }, "type": "library", "autoload": { @@ -521,22 +652,22 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.14.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0" }, - "time": "2021-12-25T01:21:49+00:00" + "time": "2022-09-18T07:06:19+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.4.1", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379" + "reference": "67c26b443f348a51926030c83481b85718457d3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", + "reference": "67c26b443f348a51926030c83481b85718457d3d", "shasum": "" }, "require": { @@ -626,7 +757,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.1" + "source": "https://github.com/guzzle/psr7/tree/2.4.3" }, "funding": [ { @@ -642,20 +773,20 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:45:39+00:00" + "time": "2022-10-26T14:07:24+00:00" }, { "name": "hisorange/browser-detect", - "version": "4.5.2", + "version": "4.5.4", "source": { "type": "git", "url": "https://github.com/hisorange/browser-detect.git", - "reference": "701b1f15be45118f126266166f6beea351296452" + "reference": "2a7c2b140209aa8477424614eb3aa6f7a7b9f429" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hisorange/browser-detect/zipball/701b1f15be45118f126266166f6beea351296452", - "reference": "701b1f15be45118f126266166f6beea351296452", + "url": "https://api.github.com/repos/hisorange/browser-detect/zipball/2a7c2b140209aa8477424614eb3aa6f7a7b9f429", + "reference": "2a7c2b140209aa8477424614eb3aa6f7a7b9f429", "shasum": "" }, "require": { @@ -715,9 +846,9 @@ ], "support": { "issues": "https://github.com/hisorange/browser-detect/issues", - "source": "https://github.com/hisorange/browser-detect/tree/4.5.2" + "source": "https://github.com/hisorange/browser-detect/tree/4.5.4" }, - "time": "2022-07-12T10:31:47+00:00" + "time": "2022-11-10T01:43:55+00:00" }, { "name": "intervention/image", @@ -805,16 +936,16 @@ }, { "name": "jaybizzle/crawler-detect", - "version": "v1.2.111", + "version": "v1.2.113", "source": { "type": "git", "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "d572ed4a65a70a2d2871dc5137c9c5b7e69745ab" + "reference": "6710b75871da2b718550c2bc33388315a3b20151" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/d572ed4a65a70a2d2871dc5137c9c5b7e69745ab", - "reference": "d572ed4a65a70a2d2871dc5137c9c5b7e69745ab", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/6710b75871da2b718550c2bc33388315a3b20151", + "reference": "6710b75871da2b718550c2bc33388315a3b20151", "shasum": "" }, "require": { @@ -851,26 +982,26 @@ ], "support": { "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", - "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.111" + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.113" }, - "time": "2022-03-15T22:19:01+00:00" + "time": "2023-02-02T21:01:40+00:00" }, { "name": "laminas/laminas-diactoros", - "version": "2.17.0", + "version": "2.24.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "5b32597aa46b83c8b85bb1cf9a6ed4fe7dd980c5" + "reference": "6028af6c3b5ced4d063a680d2483cce67578b902" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/5b32597aa46b83c8b85bb1cf9a6ed4fe7dd980c5", - "reference": "5b32597aa46b83c8b85bb1cf9a6ed4fe7dd980c5", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6028af6c3b5ced4d063a680d2483cce67578b902", + "reference": "6028af6c3b5ced4d063a680d2483cce67578b902", "shasum": "" }, "require": { - "php": "^7.4 || ~8.0.0 || ~8.1.0", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0" }, @@ -888,10 +1019,10 @@ "ext-libxml": "*", "http-interop/http-factory-tests": "^0.9.0", "laminas/laminas-coding-standard": "^2.4.0", - "php-http/psr7-integration-tests": "^1.1.1", - "phpunit/phpunit": "^9.5.23", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.24.0" + "php-http/psr7-integration-tests": "^1.2", + "phpunit/phpunit": "^9.5.27", + "psalm/plugin-phpunit": "^0.18.4", + "vimeo/psalm": "^5.4" }, "type": "library", "extra": { @@ -950,34 +1081,34 @@ "type": "community_bridge" } ], - "time": "2022-08-30T17:01:46+00:00" + "time": "2022-12-20T12:22:40+00:00" }, { "name": "laminas/laminas-httphandlerrunner", - "version": "2.1.0", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "4d337cde83e6b901a4443b0ab5c3b97cbaa46413" + "reference": "7a47834aaad7852816d2ec4fdbb0492163b039ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/4d337cde83e6b901a4443b0ab5c3b97cbaa46413", - "reference": "4d337cde83e6b901a4443b0ab5c3b97cbaa46413", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/7a47834aaad7852816d2ec4fdbb0492163b039ae", + "reference": "7a47834aaad7852816d2ec4fdbb0492163b039ae", "shasum": "" }, "require": { - "php": "^7.3 || ~8.0.0 || ~8.1.0", + "php": "~8.0.0 || ~8.1.0 || ~8.2.0", "psr/http-message": "^1.0", "psr/http-message-implementation": "^1.0", "psr/http-server-handler": "^1.0" }, "require-dev": { - "laminas/laminas-coding-standard": "~2.3.0", - "laminas/laminas-diactoros": "^2.8.0", - "phpunit/phpunit": "^9.5.9", - "psalm/plugin-phpunit": "^0.16.1", - "vimeo/psalm": "^4.10.0" + "laminas/laminas-coding-standard": "~2.4.0", + "laminas/laminas-diactoros": "^2.18", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.18.0", + "vimeo/psalm": "^5.0.0" }, "type": "library", "extra": { @@ -1017,7 +1148,7 @@ "type": "community_bridge" } ], - "time": "2021-09-22T09:27:36+00:00" + "time": "2023-01-05T21:54:03+00:00" }, { "name": "league/climate", @@ -1168,34 +1299,37 @@ }, { "name": "league/csv", - "version": "9.8.0", + "version": "9.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47" + "reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/9d2e0265c5d90f5dd601bc65ff717e05cec19b47", - "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/b4418ede47fbd88facc34e40a16c8ce9153b961b", + "reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "php": "^7.4 || ^8.0" + "php": "^8.1.2" }, "require-dev": { - "ext-curl": "*", + "doctrine/collections": "^2.1.2", "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^v3.4.0", - "phpstan/phpstan": "^1.3.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.1.0", - "phpunit/phpunit": "^9.5.11" + "ext-xdebug": "*", + "friendsofphp/php-cs-fixer": "^v3.14.3", + "phpbench/phpbench": "^1.2.8", + "phpstan/phpstan": "^1.10.4", + "phpstan/phpstan-deprecation-rules": "^1.1.2", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^10.0.14" }, "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" }, "type": "library", @@ -1248,7 +1382,7 @@ "type": "github" } ], - "time": "2022-01-04T00:13:07+00:00" + "time": "2023-03-11T15:57:12+00:00" }, { "name": "league/pipeline", @@ -1309,31 +1443,32 @@ }, { "name": "maennchen/zipstream-php", - "version": "2.2.1", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "211e9ba1530ea5260b45d90c9ea252f56ec52729" + "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/211e9ba1530ea5260b45d90c9ea252f56ec52729", - "reference": "211e9ba1530ea5260b45d90c9ea252f56ec52729", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3", + "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3", "shasum": "" }, "require": { + "ext-mbstring": "*", "myclabs/php-enum": "^1.5", - "php": "^7.4 || ^8.0", - "psr/http-message": "^1.0", - "symfony/polyfill-mbstring": "^1.0" + "php": "^8.0", + "psr/http-message": "^1.0" }, "require-dev": { "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.9", "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.4", "phpunit/phpunit": "^8.5.8 || ^9.4.2", - "vimeo/psalm": "^4.1" + "vimeo/psalm": "^5.0" }, "type": "library", "autoload": { @@ -1370,38 +1505,42 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.1" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/v2.4.0" }, "funding": [ + { + "url": "https://github.com/maennchen", + "type": "github" + }, { "url": "https://opencollective.com/zipstream", "type": "open_collective" } ], - "time": "2022-05-18T15:52:06+00:00" + "time": "2022-12-08T12:29:14+00:00" }, { "name": "markbaker/complex", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/MarkBaker/PHPComplex.git", - "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22" + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/ab8bc271e404909db09ff2d5ffa1e538085c0f22", - "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "phpcompatibility/php-compatibility": "^9.0", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", - "squizlabs/php_codesniffer": "^3.4" + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -1427,36 +1566,36 @@ ], "support": { "issues": "https://github.com/MarkBaker/PHPComplex/issues", - "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.1" + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" }, - "time": "2021-06-29T15:32:53+00:00" + "time": "2022-12-06T16:21:08+00:00" }, { "name": "markbaker/matrix", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/MarkBaker/PHPMatrix.git", - "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576" + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/c66aefcafb4f6c269510e9ac46b82619a904c576", - "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "phpcompatibility/php-compatibility": "^9.0", + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", "phpdocumentor/phpdocumentor": "2.*", "phploc/phploc": "^4.0", "phpmd/phpmd": "2.*", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", "sebastian/phpcpd": "^4.0", - "squizlabs/php_codesniffer": "^3.4" + "squizlabs/php_codesniffer": "^3.7" }, "type": "library", "autoload": { @@ -1483,9 +1622,9 @@ ], "support": { "issues": "https://github.com/MarkBaker/PHPMatrix/issues", - "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.0" + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" }, - "time": "2021-07-01T19:01:15+00:00" + "time": "2022-12-02T22:17:43+00:00" }, { "name": "markstory/asset_compress", @@ -1554,21 +1693,21 @@ }, { "name": "markstory/mini-asset", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/markstory/mini-asset.git", - "reference": "5b3f7d7876d3f7bd9377664208cc386341025e5f" + "reference": "017350eb1a546608d0aaaacf1853af2be95248fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/markstory/mini-asset/zipball/5b3f7d7876d3f7bd9377664208cc386341025e5f", - "reference": "5b3f7d7876d3f7bd9377664208cc386341025e5f", + "url": "https://api.github.com/repos/markstory/mini-asset/zipball/017350eb1a546608d0aaaacf1853af2be95248fc", + "reference": "017350eb1a546608d0aaaacf1853af2be95248fc", "shasum": "" }, "require": { "league/climate": "~3.0", - "php": ">=7.2,<8.2" + "php": ">=7.4,<8.3" }, "require-dev": { "laminas/laminas-diactoros": "~2.0", @@ -1619,20 +1758,20 @@ "issues": "https://github.com/markstory/mini-asset/issues", "source": "https://github.com/markstory/mini-asset" }, - "time": "2022-08-25T14:08:56+00:00" + "time": "2023-01-09T03:01:07+00:00" }, { "name": "matomo/device-detector", - "version": "6.0.2", + "version": "6.0.6", "source": { "type": "git", "url": "https://github.com/matomo-org/device-detector.git", - "reference": "c01910dde14c95708019513ea0ee36c5a174fd4c" + "reference": "ce5ef5e6776c16af306d38e20674973f072e05ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/c01910dde14c95708019513ea0ee36c5a174fd4c", - "reference": "c01910dde14c95708019513ea0ee36c5a174fd4c", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/ce5ef5e6776c16af306d38e20674973f072e05ed", + "reference": "ce5ef5e6776c16af306d38e20674973f072e05ed", "shasum": "" }, "require": { @@ -1688,20 +1827,20 @@ "source": "https://github.com/matomo-org/matomo", "wiki": "https://dev.matomo.org/" }, - "time": "2022-07-12T07:49:25+00:00" + "time": "2023-01-16T08:18:02+00:00" }, { "name": "mobiledetect/mobiledetectlib", - "version": "2.8.39", + "version": "2.8.41", "source": { "type": "git", "url": "https://github.com/serbanghita/Mobile-Detect.git", - "reference": "0fd6753003fc870f6e229bae869cc1337c99bc45" + "reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/0fd6753003fc870f6e229bae869cc1337c99bc45", - "reference": "0fd6753003fc870f6e229bae869cc1337c99bc45", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1", + "reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1", "shasum": "" }, "require": { @@ -1742,9 +1881,9 @@ ], "support": { "issues": "https://github.com/serbanghita/Mobile-Detect/issues", - "source": "https://github.com/serbanghita/Mobile-Detect/tree/2.8.39" + "source": "https://github.com/serbanghita/Mobile-Detect/tree/2.8.41" }, - "time": "2022-02-17T19:24:25+00:00" + "time": "2022-11-08T18:31:26+00:00" }, { "name": "mustangostang/spyc", @@ -1859,18 +1998,74 @@ ], "time": "2022-08-04T09:53:51+00:00" }, + { + "name": "nikic/php-parser", + "version": "v4.15.3", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + }, + "time": "2023-01-16T22:05:37+00:00" + }, { "name": "phpoffice/phpspreadsheet", - "version": "1.24.1", + "version": "1.28.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "69991111e05fca3ff7398e1e7fca9ebed33efec6" + "reference": "6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/69991111e05fca3ff7398e1e7fca9ebed33efec6", - "reference": "69991111e05fca3ff7398e1e7fca9ebed33efec6", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a", + "reference": "6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a", "shasum": "" }, "require": { @@ -1887,33 +2082,34 @@ "ext-xmlwriter": "*", "ext-zip": "*", "ext-zlib": "*", - "ezyang/htmlpurifier": "^4.13", + "ezyang/htmlpurifier": "^4.15", "maennchen/zipstream-php": "^2.1", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", - "psr/simple-cache": "^1.0 || ^2.0" + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", "dompdf/dompdf": "^1.0 || ^2.0", "friendsofphp/php-cs-fixer": "^3.2", - "jpgraph/jpgraph": "^4.0", - "mpdf/mpdf": "8.1.1", + "mitoteam/jpgraph": "^10.2.4", + "mpdf/mpdf": "^8.1.1", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^8.5 || ^9.0", "squizlabs/php_codesniffer": "^3.7", - "tecnickcom/tcpdf": "^6.4" + "tecnickcom/tcpdf": "^6.5" }, "suggest": { - "dompdf/dompdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)", - "jpgraph/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", "mpdf/mpdf": "Option for rendering PDF with PDF Writer", - "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer (doesn't yet support PHP8)" + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" }, "type": "library", "autoload": { @@ -1959,9 +2155,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.24.1" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.28.0" }, - "time": "2022-07-18T19:50:48+00:00" + "time": "2023-02-25T12:24:49+00:00" }, { "name": "psr/container", @@ -2437,16 +2633,16 @@ }, { "name": "robmorgan/phinx", - "version": "0.12.12", + "version": "0.13.4", "source": { "type": "git", "url": "https://github.com/cakephp/phinx.git", - "reference": "9a6ce1e7fdf0fa4e602ba5875b5bc9442ccaa115" + "reference": "18e06e4a2b18947663438afd2f467e17c62e867d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/phinx/zipball/9a6ce1e7fdf0fa4e602ba5875b5bc9442ccaa115", - "reference": "9a6ce1e7fdf0fa4e602ba5875b5bc9442ccaa115", + "url": "https://api.github.com/repos/cakephp/phinx/zipball/18e06e4a2b18947663438afd2f467e17c62e867d", + "reference": "18e06e4a2b18947663438afd2f467e17c62e867d", "shasum": "" }, "require": { @@ -2517,9 +2713,9 @@ ], "support": { "issues": "https://github.com/cakephp/phinx/issues", - "source": "https://github.com/cakephp/phinx/tree/0.12.12" + "source": "https://github.com/cakephp/phinx/tree/0.13.4" }, - "time": "2022-07-09T18:53:51+00:00" + "time": "2023-01-07T00:42:55+00:00" }, { "name": "seld/cli-prompt", @@ -2646,16 +2842,16 @@ }, { "name": "symfony/config", - "version": "v6.1.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "a0645dc585d378b73c01115dd7ab9348f7d40c85" + "reference": "f31b3c78a3650157188a240695e688d6a182aa91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/a0645dc585d378b73c01115dd7ab9348f7d40c85", - "reference": "a0645dc585d378b73c01115dd7ab9348f7d40c85", + "url": "https://api.github.com/repos/symfony/config/zipball/f31b3c78a3650157188a240695e688d6a182aa91", + "reference": "f31b3c78a3650157188a240695e688d6a182aa91", "shasum": "" }, "require": { @@ -2703,7 +2899,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.1.3" + "source": "https://github.com/symfony/config/tree/v6.2.5" }, "funding": [ { @@ -2719,20 +2915,20 @@ "type": "tidelift" } ], - "time": "2022-07-20T15:00:40+00:00" + "time": "2023-01-09T04:38:22+00:00" }, { "name": "symfony/console", - "version": "v6.1.4", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "7fccea8728aa2d431a6725b02b3ce759049fc84d" + "reference": "3e294254f2191762c1d137aed4b94e966965e985" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/7fccea8728aa2d431a6725b02b3ce759049fc84d", - "reference": "7fccea8728aa2d431a6725b02b3ce759049fc84d", + "url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985", + "reference": "3e294254f2191762c1d137aed4b94e966965e985", "shasum": "" }, "require": { @@ -2799,7 +2995,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.1.4" + "source": "https://github.com/symfony/console/tree/v6.2.5" }, "funding": [ { @@ -2815,20 +3011,20 @@ "type": "tidelift" } ], - "time": "2022-08-26T10:32:31+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.1.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { @@ -2837,7 +3033,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -2866,7 +3062,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { @@ -2882,20 +3078,20 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/filesystem", - "version": "v6.1.4", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3f39c04d2630c34019907b02f85672dac99f8659" + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3f39c04d2630c34019907b02f85672dac99f8659", - "reference": "3f39c04d2630c34019907b02f85672dac99f8659", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593", + "reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593", "shasum": "" }, "require": { @@ -2929,7 +3125,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.1.4" + "source": "https://github.com/symfony/filesystem/tree/v6.2.5" }, "funding": [ { @@ -2945,20 +3141,20 @@ "type": "tidelift" } ], - "time": "2022-08-02T16:17:38+00:00" + "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -2973,7 +3169,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3011,7 +3207,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -3027,20 +3223,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "433d05519ce6990bf3530fba6957499d327395c2" + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", - "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { @@ -3052,7 +3248,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3092,7 +3288,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { @@ -3108,20 +3304,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", - "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { @@ -3133,7 +3329,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3176,7 +3372,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { @@ -3192,20 +3388,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -3220,7 +3416,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -3259,7 +3455,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -3275,20 +3471,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.1.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239" + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239", - "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", "shasum": "" }, "require": { @@ -3304,7 +3500,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -3344,7 +3540,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.1.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" }, "funding": [ { @@ -3360,20 +3556,20 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:18:58+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/string", - "version": "v6.1.4", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "290972cad7b364e3befaa74ba0ec729800fb161c" + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/290972cad7b364e3befaa74ba0ec729800fb161c", - "reference": "290972cad7b364e3befaa74ba0ec729800fb161c", + "url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", + "reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0", "shasum": "" }, "require": { @@ -3389,6 +3585,7 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -3429,7 +3626,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.1.4" + "source": "https://github.com/symfony/string/tree/v6.2.5" }, "funding": [ { @@ -3445,20 +3642,20 @@ "type": "tidelift" } ], - "time": "2022-08-12T18:05:43+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "tecnickcom/tcpdf", - "version": "6.5.0", + "version": "6.6.2", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "cc54c1503685e618b23922f53635f46e87653662" + "reference": "e3cffc9bcbc76e89e167e9eb0bbda0cab7518459" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/cc54c1503685e618b23922f53635f46e87653662", - "reference": "cc54c1503685e618b23922f53635f46e87653662", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/e3cffc9bcbc76e89e167e9eb0bbda0cab7518459", + "reference": "e3cffc9bcbc76e89e167e9eb0bbda0cab7518459", "shasum": "" }, "require": { @@ -3509,7 +3706,7 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.5.0" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.6.2" }, "funding": [ { @@ -3517,7 +3714,7 @@ "type": "custom" } ], - "time": "2022-08-12T07:50:54+00:00" + "time": "2022-12-17T10:28:59+00:00" }, { "name": "ua-parser/uap-php", @@ -3584,6 +3781,63 @@ } ], "packages-dev": [ + { + "name": "cakephp/bake", + "version": "2.9.3", + "source": { + "type": "git", + "url": "https://github.com/cakephp/bake.git", + "reference": "a9b02fb6a5f96e8fb9887be55cccea501468907b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/bake/zipball/a9b02fb6a5f96e8fb9887be55cccea501468907b", + "reference": "a9b02fb6a5f96e8fb9887be55cccea501468907b", + "shasum": "" + }, + "require": { + "brick/varexporter": "^0.3.5", + "cakephp/cakephp": "^4.3.0", + "cakephp/twig-view": "^1.0.2", + "nikic/php-parser": "^4.13.2", + "php": ">=7.2" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^4.0", + "cakephp/debug_kit": "^4.1", + "cakephp/plugin-installer": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "Bake\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/bake/graphs/contributors" + } + ], + "description": "Bake plugin for CakePHP", + "homepage": "https://github.com/cakephp/bake", + "keywords": [ + "bake", + "cakephp" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/bake/issues", + "source": "https://github.com/cakephp/bake" + }, + "time": "2023-03-18T19:26:16+00:00" + }, { "name": "cakephp/cakephp-codesniffer", "version": "4.6.0", @@ -3638,16 +3892,16 @@ }, { "name": "cakephp/debug_kit", - "version": "4.9.0", + "version": "4.9.3", "source": { "type": "git", "url": "https://github.com/cakephp/debug_kit.git", - "reference": "cd05c7b0d5600308683dc9911c3ff3e4fbe9d590" + "reference": "db848e787bf53483069cba2feb449c2e8523f525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/cd05c7b0d5600308683dc9911c3ff3e4fbe9d590", - "reference": "cd05c7b0d5600308683dc9911c3ff3e4fbe9d590", + "url": "https://api.github.com/repos/cakephp/debug_kit/zipball/db848e787bf53483069cba2feb449c2e8523f525", + "reference": "db848e787bf53483069cba2feb449c2e8523f525", "shasum": "" }, "require": { @@ -3700,7 +3954,70 @@ "issues": "https://github.com/cakephp/debug_kit/issues", "source": "https://github.com/cakephp/debug_kit" }, - "time": "2022-07-17T11:47:08+00:00" + "time": "2023-01-25T04:49:08+00:00" + }, + { + "name": "cakephp/twig-view", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/twig-view.git", + "reference": "14df50360b809a171d0688020fbdfe513763f89b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/twig-view/zipball/14df50360b809a171d0688020fbdfe513763f89b", + "reference": "14df50360b809a171d0688020fbdfe513763f89b", + "shasum": "" + }, + "require": { + "cakephp/cakephp": "^4.0", + "jasny/twig-extensions": "^1.3", + "php": ">=7.2", + "twig/markdown-extra": "^3.0", + "twig/twig": "^3.0" + }, + "conflict": { + "wyrihaximus/twig-view": "*" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^4.0", + "cakephp/debug_kit": "^4.0", + "cakephp/plugin-installer": "^1.3", + "michelf/php-markdown": "^1.9", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "cakephp-plugin", + "autoload": { + "psr-4": { + "Cake\\TwigView\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" + } + ], + "description": "Twig powered View for CakePHP", + "keywords": [ + "cakephp", + "template", + "twig", + "view" + ], + "support": { + "forum": "https://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "issues": "https://github.com/cakephp/twig-view/issues", + "source": "https://github.com/cakephp/twig-view" + }, + "time": "2021-09-17T14:07:52+00:00" }, { "name": "composer/class-map-generator", @@ -3777,23 +4094,23 @@ }, { "name": "composer/composer", - "version": "2.4.1", + "version": "2.5.2", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "777d542e3af65f8e7a66a4d98ce7a697da339414" + "reference": "c76c013c555160410af47c03a0e173518e3f5796" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/777d542e3af65f8e7a66a4d98ce7a697da339414", - "reference": "777d542e3af65f8e7a66a4d98ce7a697da339414", + "url": "https://api.github.com/repos/composer/composer/zipball/c76c013c555160410af47c03a0e173518e3f5796", + "reference": "c76c013c555160410af47c03a0e173518e3f5796", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", "composer/class-map-generator": "^1.0", "composer/metadata-minifier": "^1.0", - "composer/pcre": "^2 || ^3", + "composer/pcre": "^2.1 || ^3.1", "composer/semver": "^3.0", "composer/spdx-licenses": "^1.5.7", "composer/xdebug-handler": "^2.0.2 || ^3.0.3", @@ -3809,14 +4126,15 @@ "symfony/finder": "^5.4 || ^6.0", "symfony/polyfill-php73": "^1.24", "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", "symfony/process": "^5.4 || ^6.0" }, "require-dev": { - "phpstan/phpstan": "^1.4.1", + "phpstan/phpstan": "^1.9.3", "phpstan/phpstan-deprecation-rules": "^1", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1", - "phpstan/phpstan-symfony": "^1.1", + "phpstan/phpstan-symfony": "^1.2.10", "symfony/phpunit-bridge": "^6.0" }, "suggest": { @@ -3830,7 +4148,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "phpstan": { "includes": [ @@ -3869,7 +4187,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/2.4.1" + "source": "https://github.com/composer/composer/tree/2.5.2" }, "funding": [ { @@ -3885,7 +4203,7 @@ "type": "tidelift" } ], - "time": "2022-08-20T09:44:50+00:00" + "time": "2023-02-04T13:33:22+00:00" }, { "name": "composer/metadata-minifier", @@ -3958,16 +4276,16 @@ }, { "name": "composer/pcre", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd" + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd", - "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", "shasum": "" }, "require": { @@ -4009,7 +4327,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.0.0" + "source": "https://github.com/composer/pcre/tree/3.1.0" }, "funding": [ { @@ -4025,7 +4343,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T20:21:48+00:00" + "time": "2022-11-17T09:50:14+00:00" }, { "name": "composer/semver", @@ -4256,35 +4574,38 @@ }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", + "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4300,7 +4621,7 @@ }, { "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", @@ -4324,37 +4645,37 @@ "tests" ], "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2022-02-04T12:51:07+00:00" + "time": "2023-01-05T11:28:13+00:00" }, { "name": "doctrine/instantiator", - "version": "1.4.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -4381,7 +4702,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -4397,27 +4718,169 @@ "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { - "name": "jdorn/sql-formatter", - "version": "v1.2.17", + "name": "ergebnis/phpstan-rules", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/jdorn/sql-formatter.git", - "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + "url": "https://github.com/ergebnis/phpstan-rules.git", + "reference": "ebeced30d7e50324a94a83c19a59d75816e8fbb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", - "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/ebeced30d7e50324a94a83c19a59d75816e8fbb4", + "reference": "ebeced30d7e50324a94a83c19a59d75816e8fbb4", "shasum": "" }, "require": { - "php": ">=5.2.4" + "ext-mbstring": "*", + "nikic/php-parser": "^4.2.3", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.0.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*" + "doctrine/orm": "^2.10.2", + "ergebnis/composer-normalize": "^2.15.0", + "ergebnis/license": "^1.1.0", + "ergebnis/php-cs-fixer-config": "^2.14.0", + "ergebnis/test-util": "^1.5.0", + "infection/infection": "~0.15.3", + "nette/di": "^3.0.11", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-strict-rules": "^1.0.0", + "phpunit/phpunit": "^8.5.21", + "psalm/plugin-phpunit": "~0.16.1", + "psr/container": "^1.0.0", + "vimeo/psalm": "^4.12.0", + "zendframework/zend-servicemanager": "^2.0.0" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\PHPStan\\Rules\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com" + } + ], + "description": "Provides additional rules for phpstan/phpstan.", + "homepage": "https://github.com/ergebnis/phpstan-rules", + "keywords": [ + "PHPStan", + "phpstan-extreme-rules", + "phpstan-rules" + ], + "support": { + "issues": "https://github.com/ergebnis/phpstan-rules/issues", + "source": "https://github.com/ergebnis/phpstan-rules" + }, + "funding": [ + { + "url": "https://github.com/localheinz", + "type": "github" + } + ], + "time": "2021-11-08T15:37:09+00:00" + }, + { + "name": "jasny/twig-extensions", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/jasny/twig-extensions.git", + "reference": "a694eb02f6fc14ff8e2fceb8b80882c0c926102b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jasny/twig-extensions/zipball/a694eb02f6fc14ff8e2fceb8b80882c0c926102b", + "reference": "a694eb02f6fc14ff8e2fceb8b80882c0c926102b", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "twig/twig": "^2.0 | ^3.0" + }, + "require-dev": { + "ext-intl": "*", + "ext-pcre": "*", + "jasny/php-code-quality": "^2.5", + "php": ">=7.2.0" + }, + "suggest": { + "ext-intl": "Required for the use of the LocalDate Twig extension", + "ext-pcre": "Required for the use of the PCRE Twig extension" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jasny\\Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Arnold Daniels", + "email": "arnold@jasny.net", + "homepage": "http://www.jasny.net" + } + ], + "description": "A set of useful Twig filters", + "homepage": "http://github.com/jasny/twig-extensions#README", + "keywords": [ + "PCRE", + "array", + "date", + "datetime", + "preg", + "regex", + "templating", + "text", + "time" + ], + "support": { + "issues": "https://github.com/jasny/twig-extensions/issues", + "source": "https://github.com/jasny/twig-extensions" + }, + "time": "2019-12-10T16:04:23+00:00" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" }, "type": "library", "extra": { @@ -4582,62 +5045,6 @@ ], "time": "2022-03-03T13:19:32+00:00" }, - { - "name": "nikic/php-parser", - "version": "v4.15.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" - }, - "time": "2022-09-04T07:30:47+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.3", @@ -4751,16 +5158,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.7.0", + "version": "1.15.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "367a8d9d5f7da2a0136422d27ce8840583926955" + "reference": "61800f71a5526081d1b5633766aa88341f1ade76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/367a8d9d5f7da2a0136422d27ce8840583926955", - "reference": "367a8d9d5f7da2a0136422d27ce8840583926955", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/61800f71a5526081d1b5633766aa88341f1ade76", + "reference": "61800f71a5526081d1b5633766aa88341f1ade76", "shasum": "" }, "require": { @@ -4790,22 +5197,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.7.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.3" }, - "time": "2022-08-09T12:23:23+00:00" + "time": "2022-12-20T20:56:55+00:00" }, { "name": "phpstan/phpstan", - "version": "1.8.4", + "version": "1.10.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "eed4c9da531f6ebb4787235b6fb486e2c20f34e5" + "reference": "f1e22c9b17a879987f8743d81533250a5fff47f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/eed4c9da531f6ebb4787235b6fb486e2c20f34e5", - "reference": "eed4c9da531f6ebb4787235b6fb486e2c20f34e5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f1e22c9b17a879987f8743d81533250a5fff47f9", + "reference": "f1e22c9b17a879987f8743d81533250a5fff47f9", "shasum": "" }, "require": { @@ -4834,8 +5241,11 @@ "static analysis" ], "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.4" + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { @@ -4851,20 +5261,20 @@ "type": "tidelift" } ], - "time": "2022-09-03T13:08:04+00:00" + "time": "2023-04-01T17:06:15+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.17", + "version": "9.2.24", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8" + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2cf940ebc6355a9d430462811b5aaa308b174bed", + "reference": "2cf940ebc6355a9d430462811b5aaa308b174bed", "shasum": "" }, "require": { @@ -4920,7 +5330,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.24" }, "funding": [ { @@ -4928,7 +5338,7 @@ "type": "github" } ], - "time": "2022-08-30T12:24:04+00:00" + "time": "2023-01-26T08:26:55+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5173,20 +5583,20 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.24", + "version": "9.6.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5" + "reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7b1615e3e887d6c719121c6d4a44b0ab9645555", + "reference": "e7b1615e3e887d6c719121c6d4a44b0ab9645555", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -5204,14 +5614,14 @@ "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", + "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.1", + "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, "suggest": { @@ -5224,7 +5634,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { @@ -5255,7 +5665,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.3" }, "funding": [ { @@ -5265,85 +5675,13 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2022-08-30T07:42:16+00:00" - }, - { - "name": "psy/psysh", - "version": "v0.11.8", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/psysh.git", - "reference": "f455acf3645262ae389b10e9beba0c358aa6994e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/f455acf3645262ae389b10e9beba0c358aa6994e", - "reference": "f455acf3645262ae389b10e9beba0c358aa6994e", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-tokenizer": "*", - "nikic/php-parser": "^4.0 || ^3.1", - "php": "^8.0 || ^7.0.8", - "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" - }, - "conflict": { - "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" - }, - "suggest": { - "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", - "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." - }, - "bin": [ - "bin/psysh" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "0.11.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Psy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ + }, { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", - "keywords": [ - "REPL", - "console", - "interactive", - "shell" - ], - "support": { - "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.8" - }, - "time": "2022-07-28T14:25:11+00:00" + "time": "2023-02-04T13:37:15+00:00" }, { "name": "react/promise", @@ -5590,16 +5928,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -5652,7 +5990,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -5660,7 +5998,7 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", @@ -5787,16 +6125,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -5838,7 +6176,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -5846,20 +6184,20 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { @@ -5915,7 +6253,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { @@ -5923,7 +6261,7 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", @@ -6160,16 +6498,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -6208,10 +6546,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -6219,7 +6557,7 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", @@ -6278,16 +6616,16 @@ }, { "name": "sebastian/type", - "version": "3.1.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb44e1cc6e557418387ad815780360057e40753e" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb44e1cc6e557418387ad815780360057e40753e", - "reference": "fb44e1cc6e557418387ad815780360057e40753e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { @@ -6299,7 +6637,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -6322,7 +6660,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.1.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -6330,7 +6668,7 @@ "type": "github" } ], - "time": "2022-08-29T06:55:37+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -6560,32 +6898,32 @@ }, { "name": "slevomat/coding-standard", - "version": "8.4.0", + "version": "8.8.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "02f27326be19633a1b6ba76745390bbf9a4be0b6" + "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/02f27326be19633a1b6ba76745390bbf9a4be0b6", - "reference": "02f27326be19633a1b6ba76745390bbf9a4be0b6", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/59e25146a4ef0a7b194c5bc55b32dd414345db89", + "reference": "59e25146a4ef0a7b194c5bc55b32dd414345db89", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": ">=1.7.0 <1.8.0", + "phpstan/phpdoc-parser": ">=1.15.2 <1.16.0", "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.8.2", - "phpstan/phpstan-deprecation-rules": "1.0.0", - "phpstan/phpstan-phpunit": "1.0.0|1.1.1", - "phpstan/phpstan-strict-rules": "1.3.0", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.21" + "phpstan/phpstan": "1.4.10|1.9.6", + "phpstan/phpstan-deprecation-rules": "1.1.1", + "phpstan/phpstan-phpunit": "1.0.0|1.3.3", + "phpstan/phpstan-strict-rules": "1.4.4", + "phpunit/phpunit": "7.5.20|8.5.21|9.5.27" }, "type": "phpcodesniffer-standard", "extra": { @@ -6603,9 +6941,13 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "keywords": [ + "dev", + "phpcs" + ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.4.0" + "source": "https://github.com/slevomat/coding-standard/tree/8.8.0" }, "funding": [ { @@ -6617,7 +6959,7 @@ "type": "tidelift" } ], - "time": "2022-08-09T19:03:45+00:00" + "time": "2023-01-09T10:46:13+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -6677,16 +7019,16 @@ }, { "name": "symfony/finder", - "version": "v6.1.3", + "version": "v6.2.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709" + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709", - "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c", + "reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c", "shasum": "" }, "require": { @@ -6721,7 +7063,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.1.3" + "source": "https://github.com/symfony/finder/tree/v6.2.5" }, "funding": [ { @@ -6737,20 +7079,20 @@ "type": "tidelift" } ], - "time": "2022-07-29T07:42:06+00:00" + "time": "2023-01-20T17:45:48+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", "shasum": "" }, "require": { @@ -6759,7 +7101,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6800,7 +7142,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" }, "funding": [ { @@ -6816,20 +7158,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -6838,7 +7180,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6883,7 +7225,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -6899,32 +7241,44 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/process", - "version": "v6.1.3", + "name": "symfony/polyfill-php81", + "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/a6506e99cfad7059b1ab5cab395854a0a0c21292", - "reference": "a6506e99cfad7059b1ab5cab395854a0a0c21292", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6933,18 +7287,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/process/tree/v6.1.3" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" }, "funding": [ { @@ -6960,52 +7320,29 @@ "type": "tidelift" } ], - "time": "2022-06-27T17:24:16+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/var-dumper", - "version": "v6.1.3", + "name": "symfony/process", + "version": "v6.2.5", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "d5a5e44a2260c5eb5e746bf4f1fbd12ee6ceb427" + "url": "https://github.com/symfony/process.git", + "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d5a5e44a2260c5eb5e746bf4f1fbd12ee6ceb427", - "reference": "d5a5e44a2260c5eb5e746bf4f1fbd12ee6ceb427", + "url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", + "reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<5.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "php": ">=8.1" }, - "bin": [ - "Resources/bin/var-dump-server" - ], "type": "library", "autoload": { - "files": [ - "Resources/functions/dump.php" - ], "psr-4": { - "Symfony\\Component\\VarDumper\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -7017,22 +7354,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.1.3" + "source": "https://github.com/symfony/process/tree/v6.2.5" }, "funding": [ { @@ -7048,7 +7381,7 @@ "type": "tidelift" } ], - "time": "2022-07-20T13:46:29+00:00" + "time": "2023-01-01T08:38:09+00:00" }, { "name": "theseer/tokenizer", @@ -7099,21 +7432,168 @@ } ], "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "twig/markdown-extra", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/markdown-extra.git", + "reference": "cad864a40c914f682ff08d909e6bdebe5c5ed134" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/cad864a40c914f682ff08d909e6bdebe5c5ed134", + "reference": "cad864a40c914f682ff08d909e6bdebe5c5ed134", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "twig/twig": "^2.7|^3.0" + }, + "require-dev": { + "erusev/parsedown": "^1.7", + "league/commonmark": "^1.0|^2.0", + "league/html-to-markdown": "^4.8|^5.0", + "michelf/php-markdown": "^1.8|^2.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\Extra\\Markdown\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Markdown", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "markdown", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/markdown-extra/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-12-27T12:23:36+00:00" + }, + { + "name": "twig/twig", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "3ffcf4b7d890770466da3b2666f82ac054e7ec72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3ffcf4b7d890770466da3b2666f82ac054e7ec72", + "reference": "3ffcf4b7d890770466da3b2666f82ac054e7ec72", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-12-27T12:28:18+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "psy/psysh": 0 - }, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.1" + "php": ">=8.2" }, "platform-dev": [], "platform-overrides": { - "php": "8.1" + "php": "8.2" }, "plugin-api-version": "2.3.0" } diff --git a/config/Locale/de_DE/date.php b/config/Locale/de_DE/date.php index 88852a7687..1549f968f5 100644 --- a/config/Locale/de_DE/date.php +++ b/config/Locale/de_DE/date.php @@ -1,4 +1,6 @@ execute("TRUNCATE TABLE phinxlog;"); - } -} diff --git a/config/Migrations/20200415073329_ShowNewProductsOnHome.php b/config/Migrations/20200415073329_ShowNewProductsOnHome.php deleted file mode 100644 index 14ba84db40..0000000000 --- a/config/Migrations/20200415073329_ShowNewProductsOnHome.php +++ /dev/null @@ -1,28 +0,0 @@ -execute($sql); - } -} diff --git a/config/Migrations/20200501192722_EnableCashlessPaymentAddTypeConfiguration.php b/config/Migrations/20200501192722_EnableCashlessPaymentAddTypeConfiguration.php deleted file mode 100644 index 53f5057e33..0000000000 --- a/config/Migrations/20200501192722_EnableCashlessPaymentAddTypeConfiguration.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("UPDATE fcs_configuration SET type='dropdown' WHERE name = 'FCS_CASHLESS_PAYMENT_ADD_TYPE'"); - } -} diff --git a/config/Migrations/20200618063024_AddProductFeedback.php b/config/Migrations/20200618063024_AddProductFeedback.php deleted file mode 100644 index 4e1bdf4b05..0000000000 --- a/config/Migrations/20200618063024_AddProductFeedback.php +++ /dev/null @@ -1,40 +0,0 @@ -execute(" - CREATE TABLE `fcs_order_detail_feedbacks` ( - `id_order_detail` int(10) UNSIGNED NOT NULL DEFAULT '0', - `text` text CHARACTER SET utf8mb4 NOT NULL, - `customer_id` int(10) UNSIGNED NOT NULL DEFAULT '0' - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - ALTER TABLE `fcs_order_detail_feedbacks` - ADD PRIMARY KEY (`id_order_detail`); - COMMIT; - "); - - switch(I18n::getLocale()) { - case 'de_DE': - $text = 'Feedback-Funktion für Produkte aktiviert?
Mitglieder können Feedback zu bestellten Produkte verfassen.
'; - break; - case 'pl_PL': - case 'en_US': - $text = 'Are members allowed to write feedback to products?'; - break; - } - - $sql = "INSERT INTO `fcs_configuration` ( - `id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd` - ) - VALUES ( - NULL, '1', 'FCS_FEEDBACK_TO_PRODUCTS_ENABLED', '".$text."', '1', 'boolean', '320', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20200703072605_CustomerCanSelectPickupDay.php b/config/Migrations/20200703072605_CustomerCanSelectPickupDay.php deleted file mode 100644 index 0e3a375a8e..0000000000 --- a/config/Migrations/20200703072605_CustomerCanSelectPickupDay.php +++ /dev/null @@ -1,23 +0,0 @@ -execute($sql); - } -} diff --git a/config/Migrations/20200831142250_RemoveEmailLogTable.php b/config/Migrations/20200831142250_RemoveEmailLogTable.php deleted file mode 100644 index 69658b8da1..0000000000 --- a/config/Migrations/20200831142250_RemoveEmailLogTable.php +++ /dev/null @@ -1,13 +0,0 @@ -execute("DROP TABLE fcs_email_logs;"); - $this->execute("DELETE FROM fcs_configuration WHERE name = 'FCS_EMAIL_LOG_ENABLED';"); - } -} diff --git a/config/Migrations/20200910091755_AddMemberSettingUseCameraForMobileBarcodeScanning.php b/config/Migrations/20200910091755_AddMemberSettingUseCameraForMobileBarcodeScanning.php deleted file mode 100644 index 273dce42a3..0000000000 --- a/config/Migrations/20200910091755_AddMemberSettingUseCameraForMobileBarcodeScanning.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_customer` ADD `use_camera_for_barcode_scanning` TINYINT(3) UNSIGNED NULL DEFAULT '0' AFTER `timebased_currency_enabled`;"); - } -} diff --git a/config/Migrations/20200925073919_GermanIbanFix.php b/config/Migrations/20200925073919_GermanIbanFix.php deleted file mode 100644 index 06ada68636..0000000000 --- a/config/Migrations/20200925073919_GermanIbanFix.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_manufacturer` CHANGE `iban` `iban` VARCHAR(22) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';"); - } -} diff --git a/config/Migrations/20201017182431_AdaptMinimalCreditBalance.php b/config/Migrations/20201017182431_AdaptMinimalCreditBalance.php deleted file mode 100644 index 9a32836663..0000000000 --- a/config/Migrations/20201017182431_AdaptMinimalCreditBalance.php +++ /dev/null @@ -1,44 +0,0 @@ -Configuration = FactoryLocator::get('Table')->get('Configurations'); - $oldValue = $this->Configuration->find('all', [ - 'conditions' => [ - 'Configurations.name' => 'FCS_MINIMAL_CREDIT_BALANCE' - ] - ])->first(); - - $newValue = $oldValue->value * -1; - if($oldValue->value == 1) { - $newValue = 0; - } - if($oldValue->value == 0) { - $newValue = -1000; - } - - $sql = "UPDATE fcs_configuration SET - text = '".$newText."', - value = ".$newValue." - WHERE name = 'FCS_MINIMAL_CREDIT_BALANCE'"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20201029084931_AddRetailMode.php b/config/Migrations/20201029084931_AddRetailMode.php deleted file mode 100644 index 737cc92671..0000000000 --- a/config/Migrations/20201029084931_AddRetailMode.php +++ /dev/null @@ -1,79 +0,0 @@ -execute($sql); - - switch(I18n::getLocale()) { - case 'de_DE': - $textA = 'Rechnungsversand an Mitglieder aktiviert?
Infos zur Verwendung im Einzelhandel
'; - $textB = 'Umsatzsteuersatz für Pfand'; - $valueB = '20,00'; - $textC = 'Header-Text für Rechnungen an Mitglieder'; - break; - default: - $textA = 'Send invoices to members?'; - $textB = 'VAT for deposit'; - $valueB = '20.00'; - $textC = 'Header text for invoices to members'; - break; - } - - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_SEND_INVOICES_TO_CUSTOMERS', '".$textA."', '0', 'readonly', '580', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_DEPOSIT_TAX_RATE', '".$textB."', '".$valueB."', 'readonly', '581', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_INVOICE_HEADER_TEXT', '".$textC."', '', 'readonly', '582', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "INSERT INTO `fcs_cronjobs` (`id`, `name`, `time_interval`, `day_of_month`, `weekday`, `not_before_time`, `active`) VALUES (NULL, 'SendInvoicesToCustomers', 'week', NULL, 'Saturday', '10:00:00', '0');"; - $this->execute($sql); - - $sql = "UPDATE fcs_cronjobs SET name = 'SendInvoicesToManufacturers' WHERE name = 'SendInvoices';"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_payments` ADD `invoice_id` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `transaction_text`;"; - $this->execute($sql); - - $sql = " - ALTER TABLE `fcs_invoices` CHANGE `send_date` `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP; - ALTER TABLE `fcs_invoices` CHANGE `user_id` `id_customer` INT(10) UNSIGNED NOT NULL DEFAULT '0'; - UPDATE `fcs_invoices` SET id_customer = 0; - ALTER TABLE `fcs_invoices` ADD `paid_in_cash` TINYINT(4) UNSIGNED NULL DEFAULT '0' AFTER `id_customer`; - CREATE TABLE `fcs_invoice_taxes` ( - `id` int(11) NOT NULL, - `invoice_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - `tax_rate` double(20,6) NOT NULL DEFAULT '0.000000', - `total_price_excl` double(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax` double(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax_incl` double(20,6) NOT NULL DEFAULT '0.000000' - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - ALTER TABLE `fcs_invoice_taxes` - ADD PRIMARY KEY (`id`), - ADD KEY `invoice_id` (`invoice_id`); - ALTER TABLE `fcs_invoice_taxes` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; - "; - $this->execute($sql); - - $sql = " - ALTER TABLE `fcs_invoices` ADD `filename` varchar(512) NOT NULL DEFAULT '' AFTER `paid_in_cash`; - ALTER TABLE `fcs_invoices` ADD `email_status` datetime DEFAULT NULL AFTER `filename`; - "; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_invoices` CHANGE `invoice_number` `invoice_number` VARCHAR(14) NOT NULL DEFAULT '0';"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20201118084516_AddRetailMode2.php b/config/Migrations/20201118084516_AddRetailMode2.php deleted file mode 100644 index 2d7a5f231b..0000000000 --- a/config/Migrations/20201118084516_AddRetailMode2.php +++ /dev/null @@ -1,21 +0,0 @@ -execute($sql); - - $sql = "ALTER TABLE `fcs_payments` CHANGE `invoice_id` `invoice_id` INT(10) UNSIGNED NULL DEFAULT NULL;"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_invoices` ADD `cancellation_invoice_id` INT(10) UNSIGNED NULL DEFAULT NULL AFTER `email_status`;"; - $this->execute($sql); - - } - -} diff --git a/config/Migrations/20201213120713_AddRetailMode3.php b/config/Migrations/20201213120713_AddRetailMode3.php deleted file mode 100644 index daa8df47b4..0000000000 --- a/config/Migrations/20201213120713_AddRetailMode3.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_manufacturer` CHANGE `name` `name` VARCHAR(64) NULL DEFAULT NULL, CHANGE `uid_number` `uid_number` VARCHAR(30) NULL DEFAULT NULL, CHANGE `additional_text_for_invoice` `additional_text_for_invoice` MEDIUMTEXT NULL DEFAULT NULL, CHANGE `iban` `iban` VARCHAR(22) NULL DEFAULT NULL, CHANGE `bic` `bic` VARCHAR(11) NULL DEFAULT NULL, CHANGE `bank_name` `bank_name` VARCHAR(255) NULL DEFAULT NULL, CHANGE `firmenbuchnummer` `firmenbuchnummer` VARCHAR(20) NULL DEFAULT NULL, CHANGE `firmengericht` `firmengericht` VARCHAR(150) NULL DEFAULT NULL, CHANGE `aufsichtsbehoerde` `aufsichtsbehoerde` VARCHAR(150) NULL DEFAULT NULL, CHANGE `kammer` `kammer` VARCHAR(150) NULL DEFAULT NULL, CHANGE `homepage` `homepage` VARCHAR(255) NULL DEFAULT NULL;"); - } -} diff --git a/config/Migrations/20201217101514_SliderWithLink.php b/config/Migrations/20201217101514_SliderWithLink.php deleted file mode 100644 index 344ad565ad..0000000000 --- a/config/Migrations/20201217101514_SliderWithLink.php +++ /dev/null @@ -1,13 +0,0 @@ -execute("ALTER TABLE `fcs_sliders` ADD `link` VARCHAR(999) NULL DEFAULT NULL AFTER `image`;"); - $this->execute("ALTER TABLE `fcs_sliders` ADD `is_private` INT(11) unsigned NOT NULL DEFAULT '0' AFTER `link`;"); - } -} diff --git a/config/Migrations/20201220182015_ImproveMemberFeeAdministration.php b/config/Migrations/20201220182015_ImproveMemberFeeAdministration.php deleted file mode 100644 index 98cb432628..0000000000 --- a/config/Migrations/20201220182015_ImproveMemberFeeAdministration.php +++ /dev/null @@ -1,23 +0,0 @@ -Die ausgewählten Produkte sind Datengrundlage der Spalte Mitgliedsbeitrag in der Mitgliederverwaltung und werden nicht in der Umsatzstatistik angezeigt.'; - break; - case 'pl_PL': - case 'en_US': - $text = 'Which products are used as member fee product?
The selected products are the basis for the column Member Fee in the members adminstration and are not shown in the turnover statistics.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_MEMBER_FEE_PRODUCTS', '".$text."', '', 'multiple_dropdown', '3300', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - } -} diff --git a/config/Migrations/20210119101923_CheckCreditBalanceLimit.php b/config/Migrations/20210119101923_CheckCreditBalanceLimit.php deleted file mode 100644 index 520fb6b40e..0000000000 --- a/config/Migrations/20210119101923_CheckCreditBalanceLimit.php +++ /dev/null @@ -1,24 +0,0 @@ -execute($sql); - } - -} diff --git a/config/Migrations/20210401071718_RemoveCustomerGroupSetting.php b/config/Migrations/20210401071718_RemoveCustomerGroupSetting.php deleted file mode 100644 index afee9e00c2..0000000000 --- a/config/Migrations/20210401071718_RemoveCustomerGroupSetting.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("DELETE from fcs_configuration WHERE name = 'FCS_CUSTOMER_GROUP';"); - } -} diff --git a/config/Migrations/20210401082727_CustomerActivateEmailCode.php b/config/Migrations/20210401082727_CustomerActivateEmailCode.php deleted file mode 100644 index 6606a4ce38..0000000000 --- a/config/Migrations/20210401082727_CustomerActivateEmailCode.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_customer` ADD `activate_email_code` VARCHAR(12) NULL DEFAULT NULL AFTER `terms_of_use_accepted_date`;"); - } -} diff --git a/config/Migrations/20210419084816_BlogPostShowOnStartPageUntilDate.php b/config/Migrations/20210419084816_BlogPostShowOnStartPageUntilDate.php deleted file mode 100644 index 37b291a86f..0000000000 --- a/config/Migrations/20210419084816_BlogPostShowOnStartPageUntilDate.php +++ /dev/null @@ -1,14 +0,0 @@ -execute("ALTER TABLE `fcs_blog_posts` ADD `show_on_start_page_until` DATE NULL DEFAULT NULL AFTER `is_featured`;"); - $this->execute("UPDATE `fcs_blog_posts` SET `show_on_start_page_until` = DATE_ADD(NOW(), INTERVAL 30 DAY) WHERE `active` = 1 AND `is_featured` = 1;"); - $this->execute("ALTER TABLE `fcs_blog_posts` DROP `is_featured`;"); - } -} diff --git a/config/Migrations/20210427144234_RemoveOldMemberFeeSetting.php b/config/Migrations/20210427144234_RemoveOldMemberFeeSetting.php deleted file mode 100644 index b4bcb83282..0000000000 --- a/config/Migrations/20210427144234_RemoveOldMemberFeeSetting.php +++ /dev/null @@ -1,14 +0,0 @@ -execute("DELETE FROM fcs_action_logs WHERE type IN('payment_member_fee_added','payment_member_fee_deleted','payment_member_fee_flexible_added');"); - $this->execute("DELETE FROM fcs_payments WHERE type IN('member_fee','member_fee_flexible');"); - $this->execute("DELETE FROM fcs_configuration WHERE NAME = 'FCS_MEMBER_FEE_BANK_ACCOUNT_DATA';"); - } -} diff --git a/config/Migrations/20210504085123_SaveTaxInOrderDetails.php b/config/Migrations/20210504085123_SaveTaxInOrderDetails.php deleted file mode 100644 index c6669f68c2..0000000000 --- a/config/Migrations/20210504085123_SaveTaxInOrderDetails.php +++ /dev/null @@ -1,66 +0,0 @@ -execute("ALTER TABLE `fcs_order_detail` ADD `tax_unit_amount` DECIMAL(16,6) NOT NULL DEFAULT '0' AFTER `id_tax`, ADD `tax_total_amount` DECIMAL(16,6) NOT NULL DEFAULT '0' AFTER `tax_unit_amount`, ADD `tax_rate` DECIMAL(10,3) NOT NULL DEFAULT '0' AFTER `tax_total_amount`;"); - - $this->OrderDetail = FactoryLocator::get('Table')->get('OrderDetails'); - - $orderDetailCountPerMigrationStep = 1000; - $orderDetails = $this->OrderDetail->find('all'); - $loopsCount = ceil($orderDetails->count() / $orderDetailCountPerMigrationStep); - - $i = 0; - while($i <= $loopsCount) { - - $orderDetails = $this->OrderDetail->find('all', [ - 'limit' => $orderDetailCountPerMigrationStep, - 'offset' => $i * $orderDetailCountPerMigrationStep, - ])->toArray(); - - $j = 0; - foreach($orderDetails as $orderDetail) { - - $sql = "SELECT t.rate FROM fcs_tax t LEFT JOIN fcs_order_detail od ON t.id_tax = od.id_tax WHERE od.id_order_detail = :orderDetailId"; - $statement = $this->OrderDetail->getConnection()->prepare($sql); - $params = ['orderDetailId' => $orderDetail->id_order_detail]; - $statement->execute($params); - $taxes = $statement->fetchAll('assoc'); - - if (!empty($taxes)) { - $orderDetails[$j]->tax_rate = $taxes[0]['rate']; - } - - $sql = "SELECT odt.* FROM fcs_order_detail od LEFT JOIN fcs_order_detail_tax odt ON odt.id_order_detail = od.id_order_detail WHERE od.id_order_detail = :orderDetailId"; - $statement = $this->OrderDetail->getConnection()->prepare($sql); - $params = ['orderDetailId' => $orderDetail->id_order_detail]; - $statement->execute($params); - $orderDetailTaxes = $statement->fetchAll('assoc'); - - if (!empty($orderDetailTaxes)) { - $orderDetails[$j]->tax_unit_amount = $orderDetailTaxes[0]['unit_amount'] ?? 0; - $orderDetails[$j]->tax_total_amount = $orderDetailTaxes[0]['total_amount'] ?? 0; - } - - $j++; - - } - - $this->OrderDetail->saveMany($orderDetails); - - echo "- Tax for " . $orderDetailCountPerMigrationStep . " order details (starting with id " . $i * $orderDetailCountPerMigrationStep. ") migrated successfully.\n"; - $i++; - } - - $this->execute("DROP TABLE `fcs_order_detail_tax`;"); - $this->execute("ALTER TABLE `fcs_order_detail` DROP `id_tax`;"); - - } -} diff --git a/config/Migrations/20210510080630_EnablePurchasePrices.php b/config/Migrations/20210510080630_EnablePurchasePrices.php deleted file mode 100644 index ff397d9b2d..0000000000 --- a/config/Migrations/20210510080630_EnablePurchasePrices.php +++ /dev/null @@ -1,69 +0,0 @@ -execute($sql); - - // add new configuration - switch(I18n::getLocale()) { - case 'de_DE': - $text = 'Einkaufspreis für Produkte erfassen?
Der Einkaufspreis ist die Datengrundlage für die Gewinn-Statistik und für Lieferscheine an die Hersteller.
'; - break; - default: - $text = 'Enable input of purchase price?
The purchase price is the base for profit statistics and bill of delivery to manufacturers.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_PURCHASE_PRICE_ENABLED', '".$text."', '0', 'readonly', '583', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_units` ADD `purchase_price_incl_per_unit` DECIMAL(10,2) UNSIGNED NULL DEFAULT NULL AFTER `price_incl_per_unit`;"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_manufacturer` ADD `default_tax_id_purchase_price` INT(8) UNSIGNED NULL DEFAULT NULL AFTER `default_tax_id`;"; - $this->execute($sql); - - $sql = "DROP TABLE IF EXISTS `fcs_purchase_prices`; - CREATE TABLE `fcs_purchase_prices` ( - `id` int(10) UNSIGNED NOT NULL, - `product_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - `product_attribute_id` int(10) UNSIGNED NOT NULL DEFAULT '0', - `tax_id` int(10) UNSIGNED DEFAULT '0', - `price` decimal(20,6) NOT NULL DEFAULT '0.000000' - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - ALTER TABLE `fcs_purchase_prices` - ADD PRIMARY KEY (`id`), - ADD KEY `product_id` (`product_id`,`product_attribute_id`); - ALTER TABLE `fcs_purchase_prices` - MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; - COMMIT;"; - $this->execute($sql); - - $sql = "DROP TABLE IF EXISTS `fcs_order_detail_purchase_prices`; - CREATE TABLE `fcs_order_detail_purchase_prices` ( - `id_order_detail` int(10) UNSIGNED NOT NULL, - `tax_rate` decimal(10,3) NOT NULL DEFAULT '0.000', - `total_price_tax_incl` decimal(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax_excl` decimal(20,6) NOT NULL DEFAULT '0.000000', - `tax_unit_amount` decimal(16,6) NOT NULL DEFAULT '0.000000', - `tax_total_amount` decimal(16,6) NOT NULL DEFAULT '0.000000' - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - ALTER TABLE `fcs_order_detail_purchase_prices` - ADD PRIMARY KEY (`id_order_detail`); - COMMIT;"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_order_detail_units` ADD `purchase_price_incl_per_unit` DECIMAL(10,2) UNSIGNED NULL DEFAULT NULL AFTER `price_incl_per_unit`;"; - $this->execute($sql); - - - } -} diff --git a/config/Migrations/20210707083827_AddRegistrierkasseApi.php b/config/Migrations/20210707083827_AddRegistrierkasseApi.php deleted file mode 100644 index 986683db64..0000000000 --- a/config/Migrations/20210707083827_AddRegistrierkasseApi.php +++ /dev/null @@ -1,28 +0,0 @@ -Alle Rechnungen (bar und unbar) über die Registrierkasse erstellen.'; - break; - default: - $text = 'Enable API to hellocash.at?
Invoices (cash and cashless) are generated by hellocash.at.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_HELLO_CASH_API_ENABLED', '".$text."', '0', 'readonly', '583', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $this->execute("UPDATE fcs_configuration SET position = 584 WHERE name ='FCS_PURCHASE_PRICE_ENABLED';"); - - $this->execute("ALTER TABLE `fcs_customer` ADD `user_id_registrierkasse` INT(10) UNSIGNED NULL DEFAULT 0 AFTER `use_camera_for_barcode_scanning`;"); - - } -} diff --git a/config/Migrations/20210802090623_AddStorageLocation.php b/config/Migrations/20210802090623_AddStorageLocation.php deleted file mode 100644 index 06e8ea1ada..0000000000 --- a/config/Migrations/20210802090623_AddStorageLocation.php +++ /dev/null @@ -1,49 +0,0 @@ -Lagerorte: Keine Kühlung / Kühlschrank / Tiefkühler. Es erscheint ein zusätzlicher Button neben "Bestellungen - Bestellungen als PDF generieren"'; - $storageLocationA = 'Keine Kühlung'; - $storageLocationB = 'Kühlschrank'; - $storageLocationC = 'Tiefkühler'; - break; - default: - $text = 'Save storage location for products?
New button next to "Orders - show order as pdf"
'; - $storageLocationA = 'No cooling'; - $storageLocationB = 'Refrigerator'; - $storageLocationC = 'Freezer'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS', '".$text."', '0', 'boolean', '3210', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $this->execute(" - CREATE TABLE `fcs_storage_locations` ( - `id` int(11) NOT NULL, - `name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `rank` tinyint(4) UNSIGNED NOT NULL DEFAULT '0' - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; - INSERT INTO `fcs_storage_locations` (`id`, `name`, `rank`) VALUES - (1, '".$storageLocationA."', 10), - (2, '".$storageLocationB."', 20), - (3, '".$storageLocationC."', 30); - ALTER TABLE `fcs_storage_locations` - ADD PRIMARY KEY (`id`); - ALTER TABLE `fcs_storage_locations` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; - COMMIT; - "); - - $this->execute("ALTER TABLE `fcs_product` ADD `id_storage_location` TINYINT(4) UNSIGNED NOT NULL DEFAULT '1' AFTER `id_tax`;"); - - } -} diff --git a/config/Migrations/20210910191430_Instagram.php b/config/Migrations/20210910191430_Instagram.php deleted file mode 100644 index 46c307d130..0000000000 --- a/config/Migrations/20210910191430_Instagram.php +++ /dev/null @@ -1,31 +0,0 @@ -execute($sql); - - $sql = "UPDATE `fcs_configuration` SET position = 900 WHERE name = 'FCS_APP_EMAIL';"; - $this->execute($sql); - $sql = "UPDATE `fcs_configuration` SET position = 910 WHERE name = 'FCS_FACEBOOK_URL';"; - $this->execute($sql); - $sql = "UPDATE `fcs_configuration` SET position = 920 WHERE name = 'FCS_FOOTER_CMS_TEXT';"; - $this->execute($sql); - $sql = "UPDATE `fcs_configuration` SET position = 930 WHERE name = 'FCS_SHOW_FOODCOOPSHOP_BACKLINK';"; - $this->execute($sql); - } -} diff --git a/config/Migrations/20210914071747_DifferentPricesForCustomers.php b/config/Migrations/20210914071747_DifferentPricesForCustomers.php deleted file mode 100644 index 2e85c099f1..0000000000 --- a/config/Migrations/20210914071747_DifferentPricesForCustomers.php +++ /dev/null @@ -1,13 +0,0 @@ -execute("ALTER TABLE `fcs_customer` ADD `shopping_price` VARCHAR(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'SP' AFTER `user_id_registrierkasse`;"); - $this->execute("ALTER TABLE `fcs_order_detail` ADD `shopping_price` VARCHAR(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'SP' AFTER `pickup_day`;"); - } -} diff --git a/config/Migrations/20210922154148_RemoveUnusedQueueTable.php b/config/Migrations/20210922154148_RemoveUnusedQueueTable.php deleted file mode 100644 index 7ecfb5c21c..0000000000 --- a/config/Migrations/20210922154148_RemoveUnusedQueueTable.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("DROP TABLE IF EXISTS queued_tasks;"); - } -} diff --git a/config/Migrations/20210923073422_RemoveSettingShowNewProductsOnHome.php b/config/Migrations/20210923073422_RemoveSettingShowNewProductsOnHome.php deleted file mode 100644 index 1006d34e84..0000000000 --- a/config/Migrations/20210923073422_RemoveSettingShowNewProductsOnHome.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("DELETE FROM `fcs_configuration` WHERE `name` = 'FCS_SHOW_NEW_PRODUCTS_ON_HOME';"); - } -} diff --git a/config/Migrations/20210923090820_AllowNullAsPurchasePrice.php b/config/Migrations/20210923090820_AllowNullAsPurchasePrice.php deleted file mode 100644 index 43a8aab3df..0000000000 --- a/config/Migrations/20210923090820_AllowNullAsPurchasePrice.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_purchase_prices` CHANGE `price` `price` DECIMAL(20,6) NULL DEFAULT NULL;"); - } -} diff --git a/config/Migrations/20211028083847_UseExistingBarcode.php b/config/Migrations/20211028083847_UseExistingBarcode.php deleted file mode 100644 index 8425ee3c40..0000000000 --- a/config/Migrations/20211028083847_UseExistingBarcode.php +++ /dev/null @@ -1,25 +0,0 @@ -execute($sql); - } -} diff --git a/config/Migrations/20211123095227_DeactivateCheckCreditReminder.php b/config/Migrations/20211123095227_DeactivateCheckCreditReminder.php deleted file mode 100644 index 501e9a45be..0000000000 --- a/config/Migrations/20211123095227_DeactivateCheckCreditReminder.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_customer` ADD `check_credit_reminder_enabled` TINYINT UNSIGNED NULL DEFAULT '1' AFTER `shopping_price`;"); - } -} diff --git a/config/Migrations/20211213081433_ImproveCustomerNotifications.php b/config/Migrations/20211213081433_ImproveCustomerNotifications.php deleted file mode 100644 index 0a87a2ed4b..0000000000 --- a/config/Migrations/20211213081433_ImproveCustomerNotifications.php +++ /dev/null @@ -1,16 +0,0 @@ -execute("ALTER TABLE `fcs_customer` ADD `invoices_per_email_enabled` TINYINT UNSIGNED NULL DEFAULT '1' AFTER `check_credit_reminder_enabled`;"); - $this->execute("ALTER TABLE `fcs_customer` ADD `pickup_day_reminder_enabled` TINYINT UNSIGNED NULL DEFAULT '1' AFTER `invoices_per_email_enabled`;"); - $this->execute("ALTER TABLE `fcs_customer` ADD `credit_upload_reminder_enabled` TINYINT UNSIGNED NULL DEFAULT '1' AFTER `pickup_day_reminder_enabled`;"); - $this->execute("ALTER TABLE `fcs_customer` CHANGE `email_order_reminder` `email_order_reminder_enabled` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0';"); - $this->execute("ALTER TABLE `fcs_invoices` CHANGE `email_status` `email_status` VARCHAR(30) NULL DEFAULT NULL;"); - } -} diff --git a/config/Migrations/20211215184633_ManufacturerSettingIncludeStockProductsOnOrderList.php b/config/Migrations/20211215184633_ManufacturerSettingIncludeStockProductsOnOrderList.php deleted file mode 100644 index e83b932fc5..0000000000 --- a/config/Migrations/20211215184633_ManufacturerSettingIncludeStockProductsOnOrderList.php +++ /dev/null @@ -1,12 +0,0 @@ -execute("ALTER TABLE `fcs_manufacturer` ADD `include_stock_products_in_order_lists` TINYINT(3) UNSIGNED NOT NULL DEFAULT '1' AFTER `no_delivery_days`;"); - } -} diff --git a/config/Migrations/20211229194617_AddIndizesForBetterPerformance.php b/config/Migrations/20211229194617_AddIndizesForBetterPerformance.php deleted file mode 100644 index b4fb61657e..0000000000 --- a/config/Migrations/20211229194617_AddIndizesForBetterPerformance.php +++ /dev/null @@ -1,16 +0,0 @@ -execute("ALTER TABLE `fcs_category_product` ADD INDEX(`id_category`);"); - $this->execute("ALTER TABLE `fcs_product` ADD INDEX(`is_stock_product`);"); - $this->execute("ALTER TABLE `fcs_manufacturer` ADD INDEX(`stock_management_enabled`);"); - $this->execute("ALTER TABLE `fcs_category` ADD INDEX(`active`);"); - $this->execute("ALTER TABLE `fcs_barcodes` ADD INDEX(`barcode`);"); - } -} diff --git a/config/Migrations/20220129082136_SendDeliveryNotesEveryMonth.php b/config/Migrations/20220129082136_SendDeliveryNotesEveryMonth.php deleted file mode 100644 index b735141e9c..0000000000 --- a/config/Migrations/20220129082136_SendDeliveryNotesEveryMonth.php +++ /dev/null @@ -1,15 +0,0 @@ -execute($sql); - $sql = "INSERT INTO `fcs_cronjobs` (`id`, `name`, `time_interval`, `day_of_month`, `weekday`, `not_before_time`, `active`) VALUES (NULL, 'SendDeliveryNotes', 'month', '1', null, '18:00:00', '0');"; - $this->execute($sql); - } -} diff --git a/config/Migrations/20220201163254_OptionalDeliveryRhythmSettingOrderInWeekBeforeDelivery.php b/config/Migrations/20220201163254_OptionalDeliveryRhythmSettingOrderInWeekBeforeDelivery.php deleted file mode 100644 index 933938ff0b..0000000000 --- a/config/Migrations/20220201163254_OptionalDeliveryRhythmSettingOrderInWeekBeforeDelivery.php +++ /dev/null @@ -1,22 +0,0 @@ -execute($sql); - } -} diff --git a/config/Migrations/20220321103059_PrefixForInvoices.php b/config/Migrations/20220321103059_PrefixForInvoices.php deleted file mode 100644 index 0572d3c9ae..0000000000 --- a/config/Migrations/20220321103059_PrefixForInvoices.php +++ /dev/null @@ -1,27 +0,0 @@ -
Max. 6 Zeichen inkl. Trennzeichen.
'; - break; - default: - $text = 'Prefix for invoice numbers
Max. 6 chars incl. separator.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_INVOICE_NUMBER_PREFIX', '".$text."', '', 'readonly', '583', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_invoices` CHANGE `invoice_number` `invoice_number` VARCHAR(17) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0';"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20220323075926_TaxBasedOnNetInvoiceSum.php b/config/Migrations/20220323075926_TaxBasedOnNetInvoiceSum.php deleted file mode 100644 index 9c0ee829e2..0000000000 --- a/config/Migrations/20220323075926_TaxBasedOnNetInvoiceSum.php +++ /dev/null @@ -1,31 +0,0 @@ -
Die Berechnung der Umsatzsteuer erfolgt auf Basis der Netto-Rechnungsumme und ist nicht die Summe der Umsatzsteuerbeträge pro Stück.
'; - break; - default: - $text = 'Invoices for companies with fixed tax rate
Vat is calculated based on the sum of net price of the invoice.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_TAX_BASED_ON_NET_INVOICE_SUM', '".$text."', '0', 'readonly', '584', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "UPDATE fcs_configuration SET position = 585 WHERE name = 'FCS_TAX_BASED_ON_NET_INVOICE_SUM'"; - $this->execute($sql); - $sql = "UPDATE fcs_configuration SET position = 586 WHERE name = 'FCS_INVOICE_NUMBER_PREFIX'"; - $this->execute($sql); - $sql = "UPDATE fcs_configuration SET position = 587 WHERE name = 'FCS_PURCHASE_PRICE_ENABLED'"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20220407093247_AddIsCompanyFieldForCustomer.php b/config/Migrations/20220407093247_AddIsCompanyFieldForCustomer.php deleted file mode 100644 index d158b690b0..0000000000 --- a/config/Migrations/20220407093247_AddIsCompanyFieldForCustomer.php +++ /dev/null @@ -1,21 +0,0 @@ -execute($sql); - - $sql = "ALTER TABLE `fcs_customer` CHANGE `firstname` `firstname` VARCHAR(50) NOT NULL DEFAULT '', CHANGE `lastname` `lastname` VARCHAR(50) NOT NULL DEFAULT '';"; - $this->execute($sql); - - $sql = "ALTER TABLE `fcs_address` CHANGE `lastname` `lastname` VARCHAR(50) NOT NULL DEFAULT '', CHANGE `firstname` `firstname` VARCHAR(50) NOT NULL DEFAULT '';"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20220412131842_Newsletter.php b/config/Migrations/20220412131842_Newsletter.php deleted file mode 100644 index f7774fe298..0000000000 --- a/config/Migrations/20220412131842_Newsletter.php +++ /dev/null @@ -1,25 +0,0 @@ -
Mitglieder können sich bei der Registrierung für den Newsletter anmelden. Mehr Infos
'; - break; - default: - $text = 'Newsletter enabled?
Shows newsletter checkbox on registration.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_NEWSLETTER_ENABLED', '".$text."', '0', 'boolean', '3400', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $this->execute("ALTER TABLE `fcs_customer` ADD `newsletter_enabled` TINYINT UNSIGNED NULL DEFAULT '0' AFTER `credit_upload_reminder_enabled`;"); - } -} diff --git a/config/Migrations/20220620091755_RemoveTimebasedCurrencyModule.php b/config/Migrations/20220620091755_RemoveTimebasedCurrencyModule.php deleted file mode 100644 index 785cd2a19d..0000000000 --- a/config/Migrations/20220620091755_RemoveTimebasedCurrencyModule.php +++ /dev/null @@ -1,19 +0,0 @@ -execute("DROP TABLE IF EXISTS fcs_timebased_currency_order_detail"); - $this->execute("DROP TABLE IF EXISTS fcs_timebased_currency_payments"); - $this->execute("DELETE FROM fcs_configuration WHERE name LIKE '%FCS_TIMEBASED_%'"); - $this->execute("ALTER TABLE fcs_customer DROP timebased_currency_enabled"); - $this->execute("ALTER TABLE fcs_manufacturer DROP timebased_currency_enabled"); - $this->execute("ALTER TABLE fcs_manufacturer DROP timebased_currency_max_percentage"); - $this->execute("ALTER TABLE fcs_manufacturer DROP timebased_currency_max_credit_balance"); - } -} diff --git a/config/Migrations/20220717194215_UserFeedback.php b/config/Migrations/20220717194215_UserFeedback.php deleted file mode 100644 index 8fb443b704..0000000000 --- a/config/Migrations/20220717194215_UserFeedback.php +++ /dev/null @@ -1,27 +0,0 @@ -
Ermöglicht das Erfassen und Anzeigen von Feedback. Mehr Infos
'; - break; - default: - $text = 'Member and manufacturer feedback enabled?
Members and manufacturers can write feedback.
'; - break; - } - $sql = "INSERT INTO `fcs_configuration` (`id_configuration`, `active`, `name`, `text`, `value`, `type`, `position`, `locale`, `date_add`, `date_upd`) VALUES (NULL, '1', 'FCS_USER_FEEDBACK_ENABLED', '".$text."', '0', 'boolean', '3500', '".I18n::getLocale()."', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);"; - $this->execute($sql); - - $sql = "CREATE TABLE fcs_feedbacks ( `id` INT NOT NULL AUTO_INCREMENT , `customer_id` INT NULL DEFAULT NULL , `text` TEXT NULL, `approved` DATETIME NOT NULL DEFAULT '1970-01-01' , `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP , `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP , `privacy_type` TINYINT NULL DEFAULT '0' , PRIMARY KEY (`id`)) ENGINE = InnoDB;"; - $this->execute($sql); - - } -} diff --git a/config/Migrations/20220928063531_Initial.php b/config/Migrations/20220928063531_Initial.php new file mode 100644 index 0000000000..54d27b7055 --- /dev/null +++ b/config/Migrations/20220928063531_Initial.php @@ -0,0 +1,2231 @@ +table('fcs_action_logs') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('type', 'string', [ + 'default' => '', + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('customer_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('object_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('object_type', 'string', [ + 'default' => '', + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('text', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => false, + ]) + ->addColumn('date', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->create(); + + $this->table('fcs_address') + ->addColumn('id_address', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_address']) + ->addColumn('id_customer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_manufacturer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('lastname', 'string', [ + 'default' => '', + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('firstname', 'string', [ + 'default' => '', + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('address1', 'string', [ + 'default' => '', + 'limit' => 128, + 'null' => false, + ]) + ->addColumn('address2', 'string', [ + 'default' => null, + 'limit' => 128, + 'null' => true, + ]) + ->addColumn('postcode', 'string', [ + 'default' => null, + 'limit' => 12, + 'null' => true, + ]) + ->addColumn('city', 'string', [ + 'default' => '', + 'limit' => 64, + 'null' => false, + ]) + ->addColumn('comment', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('phone', 'string', [ + 'default' => null, + 'limit' => 32, + 'null' => true, + ]) + ->addColumn('phone_mobile', 'string', [ + 'default' => null, + 'limit' => 32, + 'null' => true, + ]) + ->addColumn('email', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => true, + ]) + ->addColumn('date_add', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('date_upd', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addIndex( + [ + 'id_customer', + ] + ) + ->addIndex( + [ + 'id_manufacturer', + ] + ) + ->create(); + + $this->table('fcs_attribute') + ->addColumn('id_attribute', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_attribute']) + ->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 128, + 'null' => true, + ]) + ->addColumn('can_be_used_as_unit', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('active', 'integer', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->create(); + + $this->table('fcs_barcodes') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('product_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('product_attribute_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('barcode', 'string', [ + 'default' => null, + 'limit' => 13, + 'null' => true, + ]) + ->addIndex( + [ + 'product_id', + 'product_attribute_id', + ] + ) + ->addIndex( + [ + 'barcode', + ] + ) + ->create(); + + $this->table('fcs_blog_posts') + ->addColumn('id_blog_post', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_blog_post']) + ->addColumn('title', 'string', [ + 'default' => null, + 'limit' => 150, + 'null' => false, + ]) + ->addColumn('short_description', 'string', [ + 'default' => null, + 'limit' => 100, + 'null' => false, + ]) + ->addColumn('content', 'text', [ + 'default' => null, + 'limit' => 4294967295, + 'null' => false, + ]) + ->addColumn('id_customer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_manufacturer', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('is_private', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('active', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('created', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('show_on_start_page_until', 'date', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->create(); + + $this->table('fcs_cart_product_units') + ->addColumn('id_cart_product', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('ordered_quantity_in_units', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 3, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_cart_products') + ->addColumn('id_cart_product', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_cart_product']) + ->addColumn('id_cart', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_product', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_product_attribute', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('amount', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->create(); + + $this->table('fcs_carts') + ->addColumn('id_cart', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_cart']) + ->addColumn('id_customer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('cart_type', 'integer', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('status', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->create(); + + $this->table('fcs_category') + ->addColumn('id_category', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_category']) + ->addColumn('id_parent', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 128, + 'null' => false, + ]) + ->addColumn('description', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => false, + ]) + ->addColumn('nleft', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('nright', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addIndex( + [ + 'id_parent', + ] + ) + ->addIndex( + [ + 'nleft', + 'nright', + 'active', + ] + ) + ->addIndex( + [ + 'nright', + ] + ) + ->addIndex( + [ + 'active', + 'nleft', + ] + ) + ->addIndex( + [ + 'active', + 'nright', + ] + ) + ->addIndex( + [ + 'active', + ] + ) + ->create(); + + $this->table('fcs_category_product') + ->addColumn('id_category', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_product', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_category', 'id_product']) + ->addIndex( + [ + 'id_product', + ] + ) + ->addIndex( + [ + 'id_category', + ] + ) + ->create(); + + $this->table('fcs_configuration') + ->addColumn('id_configuration', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_configuration']) + ->addColumn('active', 'boolean', [ + 'default' => false, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('name', 'string', [ + 'default' => '', + 'limit' => 254, + 'null' => false, + ]) + ->addColumn('text', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => false, + ]) + ->addColumn('value', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('type', 'string', [ + 'default' => '', + 'limit' => 20, + 'null' => false, + ]) + ->addColumn('position', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('locale', 'string', [ + 'default' => null, + 'limit' => 5, + 'null' => true, + ]) + ->addColumn('date_add', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('date_upd', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addIndex( + [ + 'name', + ] + ) + ->create(); + + $this->table('fcs_cronjob_logs') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('cronjob_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('success', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_cronjobs') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('time_interval', 'string', [ + 'default' => null, + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('day_of_month', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('weekday', 'string', [ + 'default' => null, + 'limit' => 50, + 'null' => true, + ]) + ->addColumn('not_before_time', 'time', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_customer') + ->addColumn('id_customer', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_customer']) + ->addColumn('id_default_group', 'integer', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('is_company', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('firstname', 'string', [ + 'default' => '', + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('lastname', 'string', [ + 'default' => '', + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('email', 'string', [ + 'default' => '', + 'limit' => 128, + 'null' => false, + ]) + ->addColumn('passwd', 'char', [ + 'default' => null, + 'limit' => 60, + 'null' => true, + ]) + ->addColumn('tmp_new_passwd', 'char', [ + 'default' => null, + 'limit' => 60, + 'null' => true, + ]) + ->addColumn('activate_new_password_code', 'string', [ + 'default' => null, + 'limit' => 12, + 'null' => true, + ]) + ->addColumn('auto_login_hash', 'string', [ + 'default' => null, + 'limit' => 40, + 'null' => true, + ]) + ->addColumn('email_order_reminder_enabled', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('terms_of_use_accepted_date', 'date', [ + 'default' => '1000-01-01', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('activate_email_code', 'string', [ + 'default' => null, + 'limit' => 12, + 'null' => true, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('date_add', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('date_upd', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('use_camera_for_barcode_scanning', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('user_id_registrierkasse', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('shopping_price', 'string', [ + 'default' => 'SP', + 'limit' => 2, + 'null' => true, + ]) + ->addColumn('check_credit_reminder_enabled', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('invoices_per_email_enabled', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('pickup_day_reminder_enabled', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('credit_upload_reminder_enabled', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('newsletter_enabled', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addIndex( + [ + 'email', + ] + ) + ->addIndex( + [ + 'email', + 'passwd', + ] + ) + ->addIndex( + [ + 'id_customer', + 'passwd', + ] + ) + ->create(); + + $this->table('fcs_deposits') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('id_product', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_product_attribute', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('deposit', 'float', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->create(); + + $this->table('fcs_feedbacks') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('customer_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('text', 'text', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('approved', 'datetime', [ + 'default' => '1970-01-01 00:00:00', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('privacy_type', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + ]) + ->create(); + + $this->table('fcs_images') + ->addColumn('id_image', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_image']) + ->addColumn('id_product', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_invoice_taxes') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('invoice_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('tax_rate', 'float', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('total_price_tax_excl', 'float', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('total_price_tax', 'float', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('total_price_tax_incl', 'float', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addIndex( + [ + 'invoice_id', + ] + ) + ->create(); + + $this->table('fcs_invoices') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('id_manufacturer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('invoice_number', 'string', [ + 'default' => '0', + 'limit' => 17, + 'null' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('id_customer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('paid_in_cash', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('filename', 'string', [ + 'default' => '', + 'limit' => 512, + 'null' => false, + ]) + ->addColumn('email_status', 'string', [ + 'default' => null, + 'limit' => 30, + 'null' => true, + ]) + ->addColumn('cancellation_invoice_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_manufacturer') + ->addColumn('id_manufacturer', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_manufacturer']) + ->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 64, + 'null' => true, + ]) + ->addColumn('description', 'text', [ + 'default' => null, + 'limit' => 4294967295, + 'null' => true, + ]) + ->addColumn('short_description', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('active', 'boolean', [ + 'default' => false, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('is_private', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('uid_number', 'string', [ + 'default' => null, + 'limit' => 30, + 'null' => true, + ]) + ->addColumn('additional_text_for_invoice', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('iban', 'string', [ + 'default' => null, + 'limit' => 22, + 'null' => true, + ]) + ->addColumn('bic', 'string', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]) + ->addColumn('bank_name', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => true, + ]) + ->addColumn('firmenbuchnummer', 'string', [ + 'default' => null, + 'limit' => 20, + 'null' => true, + ]) + ->addColumn('firmengericht', 'string', [ + 'default' => null, + 'limit' => 150, + 'null' => true, + ]) + ->addColumn('aufsichtsbehoerde', 'string', [ + 'default' => null, + 'limit' => 150, + 'null' => true, + ]) + ->addColumn('kammer', 'string', [ + 'default' => null, + 'limit' => 150, + 'null' => true, + ]) + ->addColumn('homepage', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => true, + ]) + ->addColumn('id_customer', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('variable_member_fee', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('send_invoice', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('send_order_list', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('default_tax_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('default_tax_id_purchase_price', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('send_order_list_cc', 'string', [ + 'default' => null, + 'limit' => 512, + 'null' => true, + ]) + ->addColumn('send_instant_order_notification', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('send_ordered_product_deleted_notification', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('send_ordered_product_price_changed_notification', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('send_ordered_product_amount_changed_notification', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('enabled_sync_domains', 'string', [ + 'default' => null, + 'limit' => 50, + 'null' => true, + ]) + ->addColumn('stock_management_enabled', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('send_product_sold_out_limit_reached_for_manufacturer', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('send_product_sold_out_limit_reached_for_contact_person', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('no_delivery_days', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('include_stock_products_in_order_lists', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('send_delivery_notes', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addIndex( + [ + 'stock_management_enabled', + ] + ) + ->create(); + + $this->table('fcs_order_detail') + ->addColumn('id_order_detail', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_order_detail']) + ->addColumn('product_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('product_attribute_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('product_name', 'string', [ + 'default' => '', + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('product_amount', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('total_price_tax_incl', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('total_price_tax_excl', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('tax_unit_amount', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 16, + 'scale' => 6, + ]) + ->addColumn('tax_total_amount', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 16, + 'scale' => 6, + ]) + ->addColumn('tax_rate', 'decimal', [ + 'default' => '0.000', + 'null' => false, + 'precision' => 10, + 'scale' => 3, + ]) + ->addColumn('deposit', 'decimal', [ + 'default' => '0.00', + 'null' => false, + 'precision' => 10, + 'scale' => 2, + ]) + ->addColumn('id_customer', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_invoice', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('id_cart_product', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('order_state', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('pickup_day', 'date', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('shopping_price', 'string', [ + 'default' => 'SP', + 'limit' => 2, + 'null' => true, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addIndex( + [ + 'product_id', + ] + ) + ->addIndex( + [ + 'product_attribute_id', + ] + ) + ->addIndex( + [ + 'id_customer', + ] + ) + ->addIndex( + [ + 'pickup_day', + ] + ) + ->addIndex( + [ + 'created', + ] + ) + ->addIndex( + [ + 'order_state', + ] + ) + ->addIndex( + [ + 'product_name', + ] + ) + ->create(); + + $this->table('fcs_order_detail_feedbacks') + ->addColumn('id_order_detail', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_order_detail']) + ->addColumn('text', 'text', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('customer_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_order_detail_purchase_prices') + ->addColumn('id_order_detail', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_order_detail']) + ->addColumn('tax_rate', 'decimal', [ + 'default' => '0.000', + 'null' => false, + 'precision' => 10, + 'scale' => 3, + ]) + ->addColumn('total_price_tax_incl', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('total_price_tax_excl', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('tax_unit_amount', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 16, + 'scale' => 6, + ]) + ->addColumn('tax_total_amount', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 16, + 'scale' => 6, + ]) + ->create(); + + $this->table('fcs_order_detail_units') + ->addColumn('id_order_detail', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('product_quantity_in_units', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 3, + 'signed' => false, + ]) + ->addColumn('price_incl_per_unit', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 2, + 'signed' => false, + ]) + ->addColumn('purchase_price_incl_per_unit', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 2, + 'signed' => false, + ]) + ->addColumn('quantity_in_units', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 3, + 'signed' => false, + ]) + ->addColumn('unit_name', 'string', [ + 'default' => '', + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('unit_amount', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('mark_as_saved', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addIndex( + [ + 'id_order_detail', + ], + ['unique' => true] + ) + ->create(); + + $this->table('fcs_pages') + ->addColumn('id_page', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_page']) + ->addColumn('title', 'string', [ + 'default' => null, + 'limit' => 128, + 'null' => false, + ]) + ->addColumn('content', 'text', [ + 'default' => null, + 'limit' => 4294967295, + 'null' => false, + ]) + ->addColumn('position', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('menu_type', 'string', [ + 'default' => 'header', + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('extern_url', 'string', [ + 'default' => '', + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('id_customer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('is_private', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('created', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('full_width', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_parent', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('lft', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('rght', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->create(); + + $this->table('fcs_payments') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('id_customer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_manufacturer', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('type', 'string', [ + 'default' => 'product', + 'limit' => 20, + 'null' => false, + ]) + ->addColumn('amount', 'decimal', [ + 'default' => '0.00', + 'null' => false, + 'precision' => 10, + 'scale' => 2, + ]) + ->addColumn('text', 'string', [ + 'default' => '', + 'limit' => 255, + 'null' => false, + ]) + ->addColumn('date_add', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('date_changed', 'datetime', [ + 'default' => 'CURRENT_TIMESTAMP', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('date_transaction_add', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('transaction_text', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('invoice_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('status', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('approval', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('approval_comment', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('changed_by', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('created_by', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_pickup_days') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('customer_id', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('pickup_day', 'date', [ + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addColumn('comment', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('products_picked_up', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addIndex( + [ + 'customer_id', + ] + ) + ->addIndex( + [ + 'pickup_day', + ] + ) + ->create(); + + $this->table('fcs_product') + ->addColumn('id_product', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_product']) + ->addColumn('id_manufacturer', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('id_tax', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_storage_location', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('price', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('name', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => false, + ]) + ->addColumn('description', 'text', [ + 'default' => null, + 'limit' => 4294967295, + 'null' => true, + ]) + ->addColumn('description_short', 'text', [ + 'default' => null, + 'limit' => 16777215, + 'null' => true, + ]) + ->addColumn('unity', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => true, + ]) + ->addColumn('is_declaration_ok', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('is_stock_product', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('active', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('delivery_rhythm_type', 'string', [ + 'default' => 'week', + 'limit' => 10, + 'null' => false, + ]) + ->addColumn('delivery_rhythm_count', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('delivery_rhythm_first_delivery_day', 'date', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('delivery_rhythm_order_possible_until', 'date', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('delivery_rhythm_send_order_list_weekday', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('delivery_rhythm_send_order_list_day', 'date', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('created', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('modified', 'datetime', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addIndex( + [ + 'id_manufacturer', + 'id_product', + ] + ) + ->addIndex( + [ + 'id_manufacturer', + ] + ) + ->addIndex( + [ + 'is_stock_product', + ] + ) + ->create(); + + $this->table('fcs_product_attribute') + ->addColumn('id_product_attribute', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_product_attribute']) + ->addColumn('id_product', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('price', 'decimal', [ + 'default' => '0.000000', + 'null' => false, + 'precision' => 20, + 'scale' => 6, + ]) + ->addColumn('default_on', 'tinyinteger', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addIndex( + [ + 'id_product', + ] + ) + ->addIndex( + [ + 'id_product_attribute', + 'id_product', + ] + ) + ->create(); + + $this->table('fcs_product_attribute_combination') + ->addColumn('id_attribute', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_product_attribute', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_attribute', 'id_product_attribute']) + ->addIndex( + [ + 'id_product_attribute', + ] + ) + ->create(); + + $this->table('fcs_purchase_prices') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('product_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('product_attribute_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('tax_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('price', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 20, + 'scale' => 6, + ]) + ->addIndex( + [ + 'product_id', + 'product_attribute_id', + ] + ) + ->create(); + + $this->table('fcs_sliders') + ->addColumn('id_slider', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_slider']) + ->addColumn('image', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => true, + ]) + ->addColumn('link', 'string', [ + 'default' => null, + 'limit' => 999, + 'null' => true, + ]) + ->addColumn('is_private', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('position', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->create(); + + $this->table('fcs_stock_available') + ->addColumn('id_stock_available', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_stock_available']) + ->addColumn('id_product', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('id_product_attribute', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('quantity', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('quantity_limit', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('sold_out_limit', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + ]) + ->addColumn('always_available', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('default_quantity_after_sending_order_lists', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addIndex( + [ + 'id_product', + 'id_product_attribute', + ], + ['unique' => true] + ) + ->addIndex( + [ + 'id_product', + ] + ) + ->addIndex( + [ + 'id_product_attribute', + ] + ) + ->create(); + + $this->table('fcs_storage_locations') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 50, + 'null' => true, + ]) + ->addColumn('rank', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_sync_domains') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('domain', 'string', [ + 'default' => '', + 'limit' => 128, + 'null' => false, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + ]) + ->create(); + + $this->table('fcs_sync_products') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('sync_domain_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('local_product_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('remote_product_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('local_product_attribute_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('remote_product_attribute_id', 'integer', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_tax') + ->addColumn('id_tax', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addPrimaryKey(['id_tax']) + ->addColumn('rate', 'decimal', [ + 'default' => '0.000', + 'null' => false, + 'precision' => 10, + 'scale' => 3, + ]) + ->addColumn('active', 'tinyinteger', [ + 'default' => '1', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->addColumn('deleted', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->create(); + + $this->table('fcs_units') + ->addColumn('id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => null, + 'null' => false, + ]) + ->addPrimaryKey(['id']) + ->addColumn('id_product', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('id_product_attribute', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('price_incl_per_unit', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 2, + 'signed' => false, + ]) + ->addColumn('purchase_price_incl_per_unit', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 2, + 'signed' => false, + ]) + ->addColumn('name', 'string', [ + 'default' => '', + 'limit' => 50, + 'null' => false, + ]) + ->addColumn('amount', 'integer', [ + 'default' => null, + 'limit' => null, + 'null' => true, + 'signed' => false, + ]) + ->addColumn('price_per_unit_enabled', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + ]) + ->addColumn('quantity_in_units', 'decimal', [ + 'default' => null, + 'null' => true, + 'precision' => 10, + 'scale' => 3, + 'signed' => false, + ]) + ->addIndex( + [ + 'id_product', + 'id_product_attribute', + ], + ['unique' => true] + ) + ->create(); + } + + /** + * Down Method. + * + * More information on this method is available here: + * https://book.cakephp.org/phinx/0/en/migrations.html#the-down-method + * @return void + */ + public function down() + { + $this->table('fcs_action_logs')->drop()->save(); + $this->table('fcs_address')->drop()->save(); + $this->table('fcs_attribute')->drop()->save(); + $this->table('fcs_barcodes')->drop()->save(); + $this->table('fcs_blog_posts')->drop()->save(); + $this->table('fcs_cart_product_units')->drop()->save(); + $this->table('fcs_cart_products')->drop()->save(); + $this->table('fcs_carts')->drop()->save(); + $this->table('fcs_category')->drop()->save(); + $this->table('fcs_category_product')->drop()->save(); + $this->table('fcs_configuration')->drop()->save(); + $this->table('fcs_cronjob_logs')->drop()->save(); + $this->table('fcs_cronjobs')->drop()->save(); + $this->table('fcs_customer')->drop()->save(); + $this->table('fcs_deposits')->drop()->save(); + $this->table('fcs_feedbacks')->drop()->save(); + $this->table('fcs_images')->drop()->save(); + $this->table('fcs_invoice_taxes')->drop()->save(); + $this->table('fcs_invoices')->drop()->save(); + $this->table('fcs_manufacturer')->drop()->save(); + $this->table('fcs_order_detail')->drop()->save(); + $this->table('fcs_order_detail_feedbacks')->drop()->save(); + $this->table('fcs_order_detail_purchase_prices')->drop()->save(); + $this->table('fcs_order_detail_units')->drop()->save(); + $this->table('fcs_pages')->drop()->save(); + $this->table('fcs_payments')->drop()->save(); + $this->table('fcs_pickup_days')->drop()->save(); + $this->table('fcs_product')->drop()->save(); + $this->table('fcs_product_attribute')->drop()->save(); + $this->table('fcs_product_attribute_combination')->drop()->save(); + $this->table('fcs_purchase_prices')->drop()->save(); + $this->table('fcs_sliders')->drop()->save(); + $this->table('fcs_stock_available')->drop()->save(); + $this->table('fcs_storage_locations')->drop()->save(); + $this->table('fcs_sync_domains')->drop()->save(); + $this->table('fcs_sync_products')->drop()->save(); + $this->table('fcs_tax')->drop()->save(); + $this->table('fcs_units')->drop()->save(); + } +} diff --git a/config/Migrations/20220525092822_BiggerQueuedJobDataField.php b/config/Migrations/20220928064125_AlterDataOnQueuedJobsToMediumtext.php similarity index 57% rename from config/Migrations/20220525092822_BiggerQueuedJobDataField.php rename to config/Migrations/20220928064125_AlterDataOnQueuedJobsToMediumtext.php index 598ea6a4f6..a4c35ce920 100644 --- a/config/Migrations/20220525092822_BiggerQueuedJobDataField.php +++ b/config/Migrations/20220928064125_AlterDataOnQueuedJobsToMediumtext.php @@ -3,7 +3,7 @@ use Migrations\AbstractMigration; -class BiggerQueuedJobDataField extends AbstractMigration +class AlterDataOnQueuedJobsToMediumtext extends AbstractMigration { /** * Change Method. @@ -14,6 +14,10 @@ class BiggerQueuedJobDataField extends AbstractMigration */ public function change() { - $this->execute("ALTER TABLE `queued_jobs` CHANGE `data` `data` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;"); + $table = $this->table('queued_jobs'); + $table->changeColumn('data', 'text', [ + 'limit' => 16777215, + ]); + $table->update(); } } diff --git a/config/Migrations/20221019062108_AnonymizedCustomerDataForManufacturers.php b/config/Migrations/20221019062108_AnonymizedCustomerDataForManufacturers.php new file mode 100644 index 0000000000..c241d4cc77 --- /dev/null +++ b/config/Migrations/20221019062108_AnonymizedCustomerDataForManufacturers.php @@ -0,0 +1,19 @@ +table('fcs_manufacturer') + ->addColumn('anonymize_customers', 'tinyinteger', [ + 'default' => '0', + 'limit' => null, + 'null' => false, + 'signed' => false, + ]) + ->update(); + } +} diff --git a/config/Migrations/prepare-for-update-from-v3.5/20220928063530_MarkInitialMigrationsAsMigrated.php b/config/Migrations/prepare-for-update-from-v3.5/20220928063530_MarkInitialMigrationsAsMigrated.php new file mode 100644 index 0000000000..58adea0a72 --- /dev/null +++ b/config/Migrations/prepare-for-update-from-v3.5/20220928063530_MarkInitialMigrationsAsMigrated.php @@ -0,0 +1,28 @@ +execute("TRUNCATE phinxlog"); + + $table = $this->table('phinxlog'); + $table->setData([ + [ + 'version' => 20220928063531, + 'migration_name' => 'Initial', + ], + [ + 'version' => 20220928064125, + 'migration_name' => 'AlterDataOnQueuedJobsToMediumtext', + ], + ]); + + $table->saveData(); + + } +} diff --git a/config/Migrations/schema-dump-default.lock b/config/Migrations/schema-dump-default.lock index bda2420c4c..217d01651f 100644 Binary files a/config/Migrations/schema-dump-default.lock and b/config/Migrations/schema-dump-default.lock differ diff --git a/config/Seeds/AddTaxesAustriaSeed.php b/config/Seeds/AddTaxesAustriaSeed.php new file mode 100644 index 0000000000..707d87ff4e --- /dev/null +++ b/config/Seeds/AddTaxesAustriaSeed.php @@ -0,0 +1,18 @@ +execute($query); + } +} diff --git a/config/Seeds/AddTaxesGermanySeed.php b/config/Seeds/AddTaxesGermanySeed.php new file mode 100644 index 0000000000..8a5d38946a --- /dev/null +++ b/config/Seeds/AddTaxesGermanySeed.php @@ -0,0 +1,19 @@ +execute($query); + } +} diff --git a/config/Seeds/InitDataSeed.php b/config/Seeds/InitDataSeed.php new file mode 100644 index 0000000000..0077a33aa7 --- /dev/null +++ b/config/Seeds/InitDataSeed.php @@ -0,0 +1,27 @@ +execute($query); + + $query = "INSERT INTO `fcs_sliders` VALUES (1,'demo-slider.jpg',NULL,0,0,1);"; + $this->execute($query); + + } +} diff --git a/config/Seeds/locale/de_DE/InitDataSeed.php b/config/Seeds/locale/de_DE/InitDataSeed.php new file mode 100644 index 0000000000..24088df3e7 --- /dev/null +++ b/config/Seeds/locale/de_DE/InitDataSeed.php @@ -0,0 +1,84 @@ +
Ab welcher verfügbaren Produkt-Anzahl soll beim Bestellen der Hinweis \"(x verfügbar\") angezeigt werden?
','10','number',600,'de_DE','2017-07-26 13:24:47','2014-06-01 01:40:34'), +(31,1,'FCS_DAYS_SHOW_PRODUCT_AS_NEW','Wie viele Tage sollen Produkte \"als neu markiert\" bleiben?','7','number',700,'de_DE','2017-07-26 13:24:47','2014-05-14 21:15:45'), +(456,1,'FCS_FOOTER_CMS_TEXT','Zusätzlicher Text für den Footer',NULL,'textarea_big',920,'de_DE','2014-06-11 17:50:55','2016-07-01 21:47:47'), +(508,1,'FCS_FACEBOOK_URL','Facebook-Url für die Einbindung im Footer','https://www.facebook.com/FoodCoopShop/','text',910,'de_DE','2015-07-08 13:23:54','2015-07-08 13:23:54'), +(538,1,'FCS_REGISTRATION_EMAIL_TEXT','Zusätzlicher Text, der in der Bestätigungsmail nach einer Registrierung versendet wird.
E-Mail-Vorschau anzeigen','','textarea_big',1700,'de_DE','2016-06-26 00:00:00','2016-06-26 00:00:00'), +(543,1,'FCS_RIGHT_INFO_BOX_HTML','Inhalt der Box in der rechten Spalte unterhalb des Warenkorbes.
Um eine Zeile grün zu hinterlegen (Überschrift) bitte als \"Überschrift 3\" formatieren.
','

Abholzeiten

\r\n\r\n

Du kannst jede Woche bis spätestens Dienstag Mitternacht bestellen und die Produkte am Freitag abholen.

\r\n','textarea_big',1500,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(544,1,'FCS_NO_DELIVERY_DAYS_GLOBAL','Lieferpause für alle Hersteller?
Hier können lieferfreie Tage (z.B. Feiertage) für die gesamte Foodcoop festgelegt werden.
','','multiple_dropdown',100,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(545,1,'FCS_ACCOUNTING_EMAIL','E-Mail-Adresse des Finanzverantwortlichen
Wer bekommt die Benachrichtigung über den erfolgten Rechnungsversand?
','','text',1100,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(546,1,'FCS_REGISTRATION_INFO_TEXT','Info-Text beim Registrierungsformular
Beim Registrierungsformlar wird unterhalb der E-Mail-Adresse dieser Text angezeigt.
','Um bei uns zu bestellen musst du Vereinsmitglied sein.','textarea_big',1600,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(547,1,'FCS_SHOW_PRODUCTS_FOR_GUESTS','Produkte für nicht eingeloggte Mitglieder sichtbar?','0','boolean',200,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(548,1,'FCS_DEFAULT_NEW_MEMBER_ACTIVE','Neue Mitglieder automatisch aktivieren?','0','boolean',500,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(549,1,'FCS_MINIMAL_CREDIT_BALANCE','Bis zu welchem Guthaben-Betrag sollen Bestellungen möglich sein?','0','number',1250,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(550,1,'FCS_BANK_ACCOUNT_DATA','Bankverbindung für die Guthaben-Einzahlungen\".','Guthaben-Konto Testbank / IBAN: AT65 5645 4154 8748 8999 / BIC: ABC87878','text',1300,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(552,1,'FCS_DELIVERY_DETAILS_FOR_MANUFACTURERS','Zusätzliche Liefer-Informationen für die Hersteller
wird in den Bestell-Listen nach dem Lieferdatum angezeigt.
',', 15:00 bis 17:00 Uhr','text',1200,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(553,1,'FCS_BACKUP_EMAIL_ADDRESS_BCC','E-Mail-Adresse, an die sämtliche vom System generierten E-Mails als BCC verschickt werden (Backup).
Kann leer gelassen werden.
','','text',1900,'de_DE','2016-10-06 00:00:00','2016-10-06 00:00:00'), +(554,1,'FCS_SHOW_FOODCOOPSHOP_BACKLINK','Auf Homepage Link auf www.foodcoopshop.com anzeigen?
Der Link wird im Footer angezeigt.
','1','boolean',930,'de_DE','2016-11-27 00:00:00','2016-11-27 00:00:00'), +(556,1,'FCS_APP_NAME','Name der Foodcoop','','text',50,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(557,1,'FCS_APP_ADDRESS','Adresse der Foodcoop
Wird im Footer von Homepage und E-Mails, Datenschutzerklärung, Nutzungsbedingungen usw. verwendet.
','','textarea',60,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(558,1,'FCS_APP_EMAIL','E-Mail-Adresse der Foodcoop
','','text',900,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(559,1,'FCS_PLATFORM_OWNER','Betreiber der Plattform
Für Datenschutzerklärung und Nutzungsbedingungen, bitte auch Adresse angeben. Kann leer gelassen werden, wenn die Foodcoop selbst die Plattform betreibt.
','','textarea',90,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(564,1,'FCS_ORDER_COMMENT_ENABLED','Kommentarfeld bei Bestell-Abschluss anzeigen?
Wird im Admin-Bereich unter \"Bestellungen\" angezeigt.
','1','boolean',130,'de_DE','2017-07-09 00:00:00','2017-07-09 00:00:00'), +(565,1,'FCS_USE_VARIABLE_MEMBER_FEE','Variablen Mitgliedsbeitrag verwenden?
Den variablen Mitgliedsbeitrag bei den Hersteller-Rechnungen abziehen? Die Produkt-Preise müssen entsprechend höher eingegeben werden.
','0','readonly',400,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), +(566,1,'FCS_DEFAULT_VARIABLE_MEMBER_FEE_PERCENTAGE','Standardwert für variablen Mitgliedsbeitrag
Der Prozentsatz kann in den Hersteller-Einstellungen auch individuell angepasst werden.
','0','readonly',500,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), +(567,1,'FCS_NETWORK_PLUGIN_ENABLED','Netzwerk-Modul aktiviert?
Infos zum Netzwerk-Modul
','0','readonly',500,'de_DE','2017-09-14 00:00:00','2017-09-14 00:00:00'), +(574,1,'FCS_SHOW_PRODUCT_PRICE_FOR_GUESTS','Produktpreis für nicht eingeloggte Mitglieder anzeigen?','0','boolean',210,'de_DE','2018-05-28 18:05:54','2018-05-28 18:05:54'), +(575,1,'FCS_CURRENCY_SYMBOL','Währungssymbol','€','readonly',520,'de_DE','2018-06-13 19:53:14','2018-06-13 19:53:14'), +(576,1,'FCS_DEFAULT_LOCALE','Sprache','de_DE','readonly',550,'de_DE','2018-06-26 10:18:55','2018-06-26 10:18:55'), +(577,1,'FCS_FOODCOOPS_MAP_ENABLED','Auf Home Karte mit anderen Foodcoops anzeigen?','1','boolean',1280,'de_DE','2019-02-11 22:25:36','2019-02-11 22:25:36'), +(578,1,'FCS_WEEKLY_PICKUP_DAY','Wöchentlicher Abholtag','5','readonly',600,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(579,1,'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA','Bestelllisten-Versand: x Tage vor dem Abholtag','2','readonly',650,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(580,1,'FCS_ORDER_POSSIBLE_FOR_STOCK_PRODUCTS_IN_ORDERS_WITH_DELIVERY_RHYTHM','Sollen Lagerprodukte mit der wöchentlichen Bestellung bestellt werden können?','1','boolean',750,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(581,1,'FCS_SHOW_NON_STOCK_PRODUCTS_IN_INSTANT_ORDERS','In der Sofort-Bestellung ausschließlich Lagerprodukte anzeigen?','0','boolean',760,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(582,1,'FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES','Lagerprodukte in Rechnungen miteinbeziehen?','1','readonly',600,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(583,1,'FCS_REGISTRATION_NOTIFICATION_EMAILS','Wer soll bei neuen Registrierungen informiert werden?
Mehrere E-Mail-Adressen mit , (ohne Leerzeichen) trennen.
','','text',550,'de_DE','2019-03-05 20:01:59','2019-03-05 20:01:59'), +(584,1,'FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED','Selbstbedienungs-Modus für Lagerprodukte aktiv?
Zur Online-Doku
','0','boolean',3000,'de_DE','2019-04-17 20:01:59','2019-04-17 20:01:59'), +(585,1,'FCS_APP_ADDITIONAL_DATA','Zusätzliche Infos zur Foodcoop
Z.B. ZVR-Zahl
','','textarea',80,'de_DE','2019-08-03 20:07:04','2019-08-03 20:07:04'), +(586,1,'FCS_SELF_SERVICE_MODE_TEST_MODE_ENABLED','Selbstbedienungs-Modus im Test-Modus ausführen?
Keine Verlinkung im Haupt-Menü und bei Lagerprodukten.
','0','boolean',3100,'de_DE','2019-12-09 13:46:27','2019-12-09 13:46:27'), +(587,1,'FCS_CASHLESS_PAYMENT_ADD_TYPE','Art der Eintragung der Guthaben-Aufladungen
Wie gelangen die Guthaben-Aufladungen vom Bankkonto in den FoodCoopShop?
','list-upload','dropdown',1450,'de_DE','2020-02-11 10:12:57','2020-02-11 10:12:57'), +(589,1,'FCS_FEEDBACK_TO_PRODUCTS_ENABLED','Feedback-Funktion für Produkte aktiviert?
Mitglieder können Feedback zu bestellten Produkte verfassen.
','0','boolean',3200,'de_DE','2020-06-19 09:02:46','2020-06-19 09:02:46'), +(590,1,'FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY','Mitglied kann Abholtag beim Bestellen selbst auswählen.','0','readonly',590,'de_DE','2020-07-06 10:34:35','2020-07-06 10:34:35'), +(591,1,'FCS_SEND_INVOICES_TO_CUSTOMERS','Einzelhandels-Modus aktiviert?
Infos zur Verwendung im Einzelhandel
','0','readonly',580,'de_DE','2020-10-29 10:06:34','2020-10-29 10:06:34'), +(592,1,'FCS_DEPOSIT_TAX_RATE','Umsatzsteuersatz für Pfand','20,00','readonly',581,'de_DE','2020-11-03 15:23:55','2020-11-03 15:23:55'), +(593,1,'FCS_INVOICE_HEADER_TEXT','Header-Text für Rechnungen an Mitglieder','','readonly',582,'de_DE','2020-11-03 15:23:55','2020-11-03 15:23:55'), +(594,1,'FCS_MEMBER_FEE_PRODUCTS','Welche Produkte werden als Mitgliedsbeitrag verwendet?
Die ausgewählten Produkte sind Datengrundlage der Spalte Mitgliedsbeitrag in der Mitgliederverwaltung und werden nicht in der Umsatzstatistik angezeigt.
','','multiple_dropdown',3300,'de_DE','2020-12-20 19:26:10','2020-12-20 19:26:10'), +(595,1,'FCS_CHECK_CREDIT_BALANCE_LIMIT','Ab welchem Guthaben-Stand soll die Erinnerungsmail versendet werden?','50','number',1450,'de_DE','2021-01-19 11:23:34','2021-01-19 11:23:34'), +(596,1,'FCS_PURCHASE_PRICE_ENABLED','Einkaufspreis für Produkte erfassen?
Der Einkaufspreis ist die Datengrundlage für die Gewinn-Statistik und für Lieferscheine an die Hersteller.
','0','readonly',587,'de_DE','2021-05-10 11:27:38','2021-05-10 11:27:38'), +(597,1,'FCS_HELLO_CASH_API_ENABLED','Schnittstelle (API) zu Registrierkasse HelloCash (hellocash.at) aktivieren?
Alle Rechnungen (bar und unbar) über die Registrierkasse erstellen.
','0','readonly',583,'de_DE','2021-07-07 10:55:03','2021-07-07 10:55:03'), +(598,1,'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS','Lagerort für Produkte erfassen und in Bestelllisten anzeigen?
Lagerorte: Keine Kühlung / Kühlschrank / Tiefkühler. Es erscheint ein zusätzlicher Button neben \"Bestellungen - Bestellungen als PDF generieren\"
','1','boolean',3210,'de_DE','2021-08-02 11:28:29','2021-08-02 11:28:29'), +(599,1,'FCS_INSTAGRAM_URL','Instagram-Url für die Einbindung im Footer','','text',920,'de_DE','2021-09-10 21:23:08','2021-09-10 21:23:08'), +(600,1,'FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY','Bestellungen beim ein- und zweiwöchigen Lieferhythmus sind nur in der Woche vor der Lieferung möglich.','0','boolean',3210,'de_DE','2022-02-01 17:48:35','2022-02-01 17:48:35'), +(601,1,'FCS_INVOICE_NUMBER_PREFIX','Präfix für Rechnungs-Nummernkreis
Max. 6 Zeichen inkl. Trennzeichen.
','','readonly',586,'de_DE','2022-03-21 12:02:48','2022-03-21 12:02:48'), +(602,1,'FCS_TAX_BASED_ON_NET_INVOICE_SUM','Rechnungslegung für pauschalierte Betriebe
Die Berechnung der Umsatzsteuer erfolgt auf Basis der Netto-Rechnungsumme und ist nicht die Summe der Umsatzsteuerbeträge pro Stück.
','0','readonly',585,'de_DE','2022-03-23 09:12:23','2022-03-23 09:12:23'), +(603,1,'FCS_NEWSLETTER_ENABLED','Newsletter-Funktion aktiv?
Mitglieder können sich bei der Registrierung für den Newsletter anmelden. Mehr Infos
','0','boolean',3400,'de_DE','2022-04-12 15:28:47','2022-04-12 15:28:47'), +(604,1,'FCS_USER_FEEDBACK_ENABLED','Mitglieder- und Hersteller-Feedback aktiv?
Ermöglicht das Erfassen und Anzeigen von Feedback. Mehr Infos
','0','boolean',3500,'de_DE','2022-07-19 14:39:27','2022-07-19 14:39:27'); +"; + $this->execute($query); + + $query = " + INSERT INTO `fcs_storage_locations` VALUES + (1,'Keine Kühlung',10), + (2,'Kühlschrank',20), + (3,'Tiefkühler',30); + "; + $this->execute($query); + + $query = " + INSERT INTO `fcs_category` VALUES + (20,2,'Alle Produkte','',3,4,1,'2016-10-19 21:05:00','2016-10-19 21:05:00'); + "; + $this->execute($query); + + } +} diff --git a/config/Seeds/locale/en_US/InitDataSeed.php b/config/Seeds/locale/en_US/InitDataSeed.php new file mode 100644 index 0000000000..ee67587352 --- /dev/null +++ b/config/Seeds/locale/en_US/InitDataSeed.php @@ -0,0 +1,83 @@ +
From which amount on there should be an information text visible \"(x available\")?
','10','number',600,'en_US','2017-07-26 13:24:47','2014-06-01 01:40:34'), +(31,1,'FCS_DAYS_SHOW_PRODUCT_AS_NEW','How many days products should be \"marked as new\"?','7','number',700,'en_US','2017-07-26 13:24:47','2014-05-14 21:15:45'), +(456,1,'FCS_FOOTER_CMS_TEXT','Additional text for footer',NULL,'textarea_big',920,'en_US','2014-06-11 17:50:55','2016-07-01 21:47:47'), +(508,1,'FCS_FACEBOOK_URL','Facebook url for embedding in footer','https://www.facebook.com/FoodCoopShop/','text',910,'en_US','2015-07-08 13:23:54','2015-07-08 13:23:54'), +(538,1,'FCS_REGISTRATION_EMAIL_TEXT','Additional text that is sent in the registration e-mail after a successful registration.
E-mail preview','','textarea_big',1700,'en_US','2016-06-26 00:00:00','2016-06-26 00:00:00'), +(543,1,'FCS_RIGHT_INFO_BOX_HTML','Content of the box in the right column below the shopping cart.
To make the background of a row green, please format as \"Heading 3\".
','

Delivery time

You can order every week until Tuesday midnight and pick the products up the following Friday.

','textarea_big',1500,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(544,1,'FCS_NO_DELIVERY_DAYS_GLOBAL','Delivery break for all manufacturers?
Here you can define delivery-free days for the whole food-coop.
','','multiple_dropdown',100,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(545,1,'FCS_ACCOUNTING_EMAIL','E-mail address for the financial manager
Who receives the notification that invoices have been sent?
','','text',1100,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(546,1,'FCS_REGISTRATION_INFO_TEXT','Info text in registration form
This info text is shown in the registration form below the e-mail address.
','You need to be a member if you want to order here.','textarea_big',1600,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(547,1,'FCS_SHOW_PRODUCTS_FOR_GUESTS','Products visible for guests?','0','boolean',200,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(548,1,'FCS_DEFAULT_NEW_MEMBER_ACTIVE','Automatically activate new members?','0','boolean',500,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(549,1,'FCS_MINIMAL_CREDIT_BALANCE','Up to which credit amount orders should be possible?','0','number',1250,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(550,1,'FCS_BANK_ACCOUNT_DATA','Bank account for credit uploads.','Credit account Example Bank / IBAN: AT65 5645 4154 8748 8999 / BIC: ABC87878','text',1300,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(552,1,'FCS_DELIVERY_DETAILS_FOR_MANUFACTURERS','Additional deliverey details for manufacturers
will be shown in the order lists after the delivery date.
',', 3pm to 5pm','text',1200,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(553,1,'FCS_BACKUP_EMAIL_ADDRESS_BCC','E-mail adress to which all automatically generated e-mail are sent to as BCC (Backup).
Can be left empty.
','','text',1900,'en_US','2016-10-06 00:00:00','2016-10-06 00:00:00'), +(554,1,'FCS_SHOW_FOODCOOPSHOP_BACKLINK','Show link to www.foodcoopshop.com?
The link is shown in the footer.
','1','boolean',930,'en_US','2016-11-27 00:00:00','2016-11-27 00:00:00'), +(556,1,'FCS_APP_NAME','Name of the food-coop','','text',50,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(557,1,'FCS_APP_ADDRESS','Adress of the food-coop
Used in footer of homepage and e-mails, privacy policy and terms of use.
','','textarea',60,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(558,1,'FCS_APP_EMAIL','E-mail adress of the food-coop
','','text',900,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(559,1,'FCS_PLATFORM_OWNER','Operator of the platform
For privacy policy and terms of use, please also add adrress. Can be left empty if the food-coop itself is operator.
','','textarea',90,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(564,1,'FCS_ORDER_COMMENT_ENABLED','Show comment field when placing an order?
Shown in admin area under \"Orders\".
','1','boolean',130,'en_US','2017-07-09 00:00:00','2017-07-09 00:00:00'), +(565,1,'FCS_USE_VARIABLE_MEMBER_FEE','Use variable member fee?
Reduce the variable member fee in the manufacturer\'s invoices? Therefore the prices need to be increased.
','0','readonly',400,'en_US','2017-08-02 00:00:00','2017-08-02 00:00:00'), +(566,1,'FCS_DEFAULT_VARIABLE_MEMBER_FEE_PERCENTAGE','Default value for variable member fee
The percentage can be changed in the manufacturer\'s settings.
','0','readonly',500,'en_US','2017-08-02 00:00:00','2017-08-02 00:00:00'), +(567,1,'FCS_NETWORK_PLUGIN_ENABLED','Network module activated?
Infos to the network module
','0','readonly',500,'en_US','2017-09-14 00:00:00','2017-09-14 00:00:00'), +(574,1,'FCS_SHOW_PRODUCT_PRICE_FOR_GUESTS','Shop product price for guests?','0','boolean',210,'en_US','2018-05-28 18:05:54','2018-05-28 18:05:54'), +(575,1,'FCS_CURRENCY_SYMBOL','Currency symbol','$','readonly',520,'en_US','2018-06-13 19:53:14','2018-06-13 19:53:14'), +(576,1,'FCS_DEFAULT_LOCALE','Language','en_US','readonly',550,'en_US','2018-06-26 10:18:55','2018-06-26 10:18:55'), +(577,1,'FCS_FOODCOOPS_MAP_ENABLED','Show map with other foodcoops on home?','1','boolean',1280,'en_US','2019-02-11 22:25:36','2019-02-11 22:25:36'), +(578,1,'FCS_WEEKLY_PICKUP_DAY','Weekly pickup day','5','readonly',600,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(579,1,'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA','Sending of order lists: x days before pickup day','2','readonly',650,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(580,1,'FCS_ORDER_POSSIBLE_FOR_STOCK_PRODUCTS_IN_ORDERS_WITH_DELIVERY_RHYTHM','Allow weekly orders for stock products?','1','boolean',750,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(581,1,'FCS_SHOW_NON_STOCK_PRODUCTS_IN_INSTANT_ORDERS','Only show stock products in instant orders?','0','boolean',760,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(582,1,'FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES','Include stock products in invoices?','1','readonly',600,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(583,1,'FCS_REGISTRATION_NOTIFICATION_EMAILS','Who should be notified on new registrations?
Please separate multiple e-mail addresses with , (no space).
','','text',550,'en_US','2019-03-05 20:01:59','2019-03-05 20:01:59'), +(584,1,'FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED','Use self-service mode for stock products?
Online documentation
','0','boolean',3000,'en_US','2019-04-17 20:01:59','2019-04-17 20:01:59'), +(585,1,'FCS_APP_ADDITIONAL_DATA','Additional food-coop infos','','textarea',80,'en_US','2019-08-03 20:07:08','2019-08-03 20:07:08'), +(586,1,'FCS_SELF_SERVICE_MODE_TEST_MODE_ENABLED','Run self-service mode in test mode?
Does not add links to main menu and to stock products.
','0','boolean',3100,'en_US','2019-12-09 13:46:32','2019-12-09 13:46:32'), +(587,1,'FCS_CASHLESS_PAYMENT_ADD_TYPE','Type of adding the payments
How do the payment addings get into FoodCoopShop?
','list-upload','dropdown',1450,'en_US','2020-02-11 10:13:01','2020-02-11 10:13:01'), +(589,1,'FCS_FEEDBACK_TO_PRODUCTS_ENABLED','Are members allowed to write feedback to products?','0','boolean',3200,'en_US','2020-06-19 09:02:50','2020-06-19 09:02:50'), +(590,1,'FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY','Pickup day can be selected by member on order confirmation.','0','readonly',590,'en_US','2020-07-06 10:34:39','2020-07-06 10:34:39'), +(591,1,'FCS_SEND_INVOICES_TO_CUSTOMERS','Retail mode activated?.','0','readonly',580,'en_US','2020-10-29 10:06:39','2020-10-29 10:06:39'), +(592,1,'FCS_DEPOSIT_TAX_RATE','VAT for deposit','20.00','readonly',581,'en_US','2020-11-03 15:24:01','2020-11-03 15:24:01'), +(593,1,'FCS_INVOICE_HEADER_TEXT','Header text for invoices to members','','readonly',582,'en_US','2020-11-03 15:24:01','2020-11-03 15:24:01'), +(594,1,'FCS_MEMBER_FEE_PRODUCTS','Which products are used as member fee product?
The selected products are the basis for the column Member Fee in the members adminstration and are not shown in the turnover statistics.
','','multiple_dropdown',3300,'en_US','2020-12-20 19:26:16','2020-12-20 19:26:16'), +(595,1,'FCS_CHECK_CREDIT_BALANCE_LIMIT','Height of credit saldo when the reminder email is sent.','50','number',1450,'en_US','2021-01-19 11:23:39','2021-01-19 11:23:39'), +(596,1,'FCS_PURCHASE_PRICE_ENABLED','Enable input of purchase price?
The purchase price is the base for profit statistics and bill of delivery to manufacturers.
','0','readonly',587,'en_US','2021-05-10 11:27:43','2021-05-10 11:27:43'), +(597,1,'FCS_HELLO_CASH_API_ENABLED','Enable API to hellocash.at?
Invoices (cash and cashless) are generated by hellocash.at.
','0','readonly',583,'en_US','2021-07-07 10:55:08','2021-07-07 10:55:08'), +(598,1,'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS','Save storage location for products?
New button next to \"Orders - show order as pdf\"
','1','boolean',3210,'en_US','2021-08-02 11:28:34','2021-08-02 11:28:34'), +(599,1,'FCS_INSTAGRAM_URL','Instagram url for embedding in footer','','text',920,'en_US','2021-09-10 21:23:13','2021-09-10 21:23:13'), +(600,1,'FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY','Ordering products with delivery rhythm one or two weeks is only possible in the week before delivery.','0','boolean',3210,'en_US','2022-02-01 17:48:40','2022-02-01 17:48:40'), +(601,1,'FCS_INVOICE_NUMBER_PREFIX','Prefix for invoice numbers
Max. 6 chars incl. separator.
','','readonly',586,'en_US','2022-03-21 12:02:57','2022-03-21 12:02:57'), +(602,1,'FCS_TAX_BASED_ON_NET_INVOICE_SUM','Invoices for companies with fixed tax rate
Vat is calculated based on the sum of net price of the invoice.
','0','readonly',585,'en_US','2022-03-23 09:12:33','2022-03-23 09:12:33'), +(603,1,'FCS_NEWSLETTER_ENABLED','Newsletter enabled?
Shows newsletter checkbox on registration.
','0','boolean',3400,'en_US','2022-04-12 15:28:56','2022-04-12 15:28:56'), +(604,1,'FCS_USER_FEEDBACK_ENABLED','Member and manufacturer feedback enabled?
Members and manufacturers can write feedback.
','0','boolean',3500,'en_US','2022-07-19 14:39:36','2022-07-19 14:39:36');"; + $this->execute($query); + + $query = " + INSERT INTO `fcs_storage_locations` VALUES + (1,'No cooling',10), + (2,'Refrigerator',20), + (3,'Freezer',30); + "; + $this->execute($query); + + $query = " + INSERT INTO `fcs_category` VALUES + (20,2,'All Products','',3,4,1,'2016-10-19 21:05:00','2016-10-19 21:05:00'); + "; + $this->execute($query); + + } +} diff --git a/tests/config/sql/test-db-data.sql b/config/Seeds/tests/InitTestDataSeed.php similarity index 79% rename from tests/config/sql/test-db-data.sql rename to config/Seeds/tests/InitTestDataSeed.php index b1fb4b64c8..1cba0aec3c 100644 --- a/tests/config/sql/test-db-data.sql +++ b/config/Seeds/tests/InitTestDataSeed.php @@ -1,4 +1,13 @@ +Lorem ipsum dolor sit amet.

',88,0,0,1,'2014-12-18 10:37:26','2015-03-16 12:41:46','2021-05-19'); /*!40000 ALTER TABLE `fcs_blog_posts` ENABLE KEYS */; @@ -91,100 +100,100 @@ /*!40000 ALTER TABLE `fcs_cart_product_units` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_cart_products` DISABLE KEYS */; -INSERT INTO `fcs_cart_products` VALUES -(1,1,346,0,1,'2018-03-01 10:17:14','2018-03-01 10:17:14'), -(2,1,340,0,1,'2018-03-01 10:17:14','2018-03-01 10:17:14'), +INSERT INTO `fcs_cart_products` VALUES +(1,1,346,0,1,'2018-03-01 10:17:14','2018-03-01 10:17:14'), +(2,1,340,0,1,'2018-03-01 10:17:14','2018-03-01 10:17:14'), (3,1,60,10,1,'2018-03-01 10:17:14','2018-03-01 10:17:14'); /*!40000 ALTER TABLE `fcs_cart_products` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_carts` DISABLE KEYS */; -INSERT INTO `fcs_carts` VALUES +INSERT INTO `fcs_carts` VALUES (1,92,1,0,'2018-03-01 10:17:14','2018-03-01 10:17:14'); /*!40000 ALTER TABLE `fcs_carts` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_category` DISABLE KEYS */; -INSERT INTO `fcs_category` VALUES -(16,0,'Fleischprodukte','',11,12,1,'2014-05-14 21:40:51','2014-05-14 21:48:48'), +INSERT INTO `fcs_category` VALUES +(16,0,'Fleischprodukte','',11,12,1,'2014-05-14 21:40:51','2014-05-14 21:48:48'), (20,0,'Alle Produkte','',3,4,1,'2014-05-14 21:53:52','2014-05-17 13:14:22'); /*!40000 ALTER TABLE `fcs_category` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_category_product` DISABLE KEYS */; -INSERT INTO `fcs_category_product` VALUES -(20,60), -(16,102), -(20,102), -(16,103), -(20,103), -(20,163), -(20,339), -(16,340), -(20,340), -(20,344), -(20,346), -(16,347), -(20,347), -(16,348), -(20,348), -(20,349), -(20,350), +INSERT INTO `fcs_category_product` VALUES +(20,60), +(16,102), +(20,102), +(16,103), +(20,103), +(20,163), +(20,339), +(16,340), +(20,340), +(20,344), +(20,346), +(16,347), +(20,347), +(16,348), +(20,348), +(20,349), +(20,350), (20,351); /*!40000 ALTER TABLE `fcs_category_product` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_configuration` DISABLE KEYS */; -INSERT INTO `fcs_configuration` VALUES -(11,1,'FCS_PRODUCT_AVAILABILITY_LOW','Geringe Verfügbarkeit
Ab welcher verfügbaren Produkt-Anzahl soll beim Bestellen der Hinweis \"(x verfügbar\") angezeigt werden?
','10','number',600,'de_DE','2017-07-26 13:19:19','2014-06-01 01:40:34'), -(31,1,'FCS_DAYS_SHOW_PRODUCT_AS_NEW','Wie viele Tage sollen Produkte \"als neu markiert\" bleiben?','7','number',700,'de_DE','2017-07-26 13:19:19','2014-05-14 21:15:45'), -(456,1,'FCS_FOOTER_CMS_TEXT','Zusätzlicher Text für den Footer',NULL,'textarea_big',920,'de_DE','2014-06-11 17:50:55','2016-07-01 21:47:47'), -(508,1,'FCS_FACEBOOK_URL','Facebook-Url für die Einbindung im Footer','https://www.facebook.com/FoodCoopShop/','text',910,'de_DE','2015-07-08 13:23:54','2015-07-08 13:23:54'), -(538,1,'FCS_REGISTRATION_EMAIL_TEXT','Zusätzlicher Text, der in der Bestätigungsmail nach einer Registrierung versendet wird.
E-Mail-Vorschau anzeigen','','textarea_big',1700,'de_DE','2016-06-26 00:00:00','2016-06-26 00:00:00'), -(543,1,'FCS_RIGHT_INFO_BOX_HTML','Inhalt der Box in der rechten Spalte unterhalb des Warenkorbes.
Um eine Zeile grün zu hinterlegen (Überschrift) bitte als \"Überschrift 3\" formatieren.
','

Abholzeiten

\r\n\r\n

Der Abholtag steht jetzt immer in der Produktbeschreibung, du kannst deine Produkte am Freitag abholen.

\r\n\r\n

Du kannst jede Woche bis spätestens Dienstag Mitternacht bestellen und sie am darauffolgenden Freitag abholen.

\r\n','textarea_big',1500,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(544,1,'FCS_NO_DELIVERY_DAYS_GLOBAL','Lieferpause für alle Hersteller?
Hier können lieferfreie Tage (z.B. Feiertage) für die gesamte Foodcoop festgelegt werden.
','','multiple_dropdown',100,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(545,1,'FCS_ACCOUNTING_EMAIL','E-Mail-Adresse des Finanzverantwortlichen
Wer bekommt die Benachrichtigung über den erfolgten Rechnungsversand?
','fcs-demo-superadmin@mailinator.com','text',1100,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(546,1,'FCS_REGISTRATION_INFO_TEXT','Info-Text beim Registrierungsformular
Beim Registrierungsformlar wird unterhalb der E-Mail-Adresse dieser Text angezeigt.
','Um bei uns zu bestellen musst du Vereinsmitglied sein.','textarea_big',1600,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(547,1,'FCS_SHOW_PRODUCTS_FOR_GUESTS','Produkte für nicht eingeloggte Mitglieder sichtbar?','0','boolean',200,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(548,1,'FCS_DEFAULT_NEW_MEMBER_ACTIVE','Neue Mitglieder automatisch aktivieren?','0','boolean',500,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(549,1,'FCS_MINIMAL_CREDIT_BALANCE','Bis zu welchem Guthaben-Betrag sollen Bestellungen möglich sein?','-100','number',1250,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(550,1,'FCS_BANK_ACCOUNT_DATA','Bankverbindung für die Guthaben-Einzahlungen\".','Guthaben-Konto Testbank / IBAN: AT65 5645 4154 8748 8999 / BIC: ABC87878','text',1300,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(552,1,'FCS_DELIVERY_DETAILS_FOR_MANUFACTURERS','Zusätzliche Liefer-Informationen für die Hersteller
wird in den Bestell-Listen nach dem Lieferdatum angezeigt.
',', 15:00 bis 17:00 Uhr','text',1200,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), -(553,1,'FCS_BACKUP_EMAIL_ADDRESS_BCC','E-Mail-Adresse, an die sämtliche vom System generierten E-Mails als BCC verschickt werden (Backup).
Kann leer gelassen werden.
','','text',1900,'de_DE','2016-10-06 00:00:00','2016-10-06 00:00:00'), -(554,1,'FCS_SHOW_FOODCOOPSHOP_BACKLINK','Auf Homepage Link auf www.foodcoopshop.com anzeigen?
Der Link wird im Footer angezeigt.
','1','boolean',930,'de_DE','2016-11-27 00:00:00','2016-11-27 00:00:00'), -(556,1,'FCS_APP_NAME','Name der Foodcoop','FoodCoop Test','text',50,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(557,1,'FCS_APP_ADDRESS','Adresse der Foodcoop
Wird im Footer von Homepage und E-Mails, Datenschutzerklärung, Nutzungsbedingungen usw. verwendet.
','Demostraße 4
A-4564 Demostadt','textarea',60,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(558,1,'FCS_APP_EMAIL','E-Mail-Adresse der Foodcoop
','demo-foodcoop@maillinator.com','text',900,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(559,1,'FCS_PLATFORM_OWNER','Betreiber der Plattform
Für Datenschutzerklärung und Nutzungsbedingungen, bitte auch Adresse angeben. Kann leer gelassen werden, wenn die Foodcoop selbst die Plattform betreibt.
','','textarea',90,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(564,1,'FCS_ORDER_COMMENT_ENABLED','Kommentarfeld bei Bestell-Abschluss anzeigen?
Wird im Admin-Bereich unter \"Bestellungen\" angezeigt.
','1','boolean',130,'de_DE','2017-07-09 00:00:00','2017-07-09 00:00:00'), -(565,1,'FCS_USE_VARIABLE_MEMBER_FEE','Variablen Mitgliedsbeitrag verwenden?
Den variablen Mitgliedsbeitrag bei den Hersteller-Rechnungen abziehen? Die Produkt-Preise müssen entsprechend höher eingegeben werden.
','0','readonly',400,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), -(566,1,'FCS_DEFAULT_VARIABLE_MEMBER_FEE_PERCENTAGE','Standardwert für variablen Mitgliedsbeitrag
Der Prozentsatz kann in den Hersteller-Einstellungen auch individuell angepasst werden.
','0','readonly',500,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), -(567,1,'FCS_NETWORK_PLUGIN_ENABLED','Netzwerk-Modul aktiviert?
Infos zum Netzwerk-Modul
','1','readonly',500,'de_DE','2017-09-14 00:00:00','2017-09-14 00:00:00'), -(574,1,'FCS_SHOW_PRODUCT_PRICE_FOR_GUESTS','Produktpreis für nicht eingeloggte Mitglieder anzeigen?','0','boolean',210,'de_DE','2018-05-28 18:05:57','2018-05-28 18:05:57'), -(575,1,'FCS_CURRENCY_SYMBOL','Währungssymbol','€','readonly',520,'de_DE','2018-06-13 19:53:14','2018-06-13 19:53:14'), -(576,1,'FCS_DEFAULT_LOCALE','Sprache','de_DE','readonly',550,'de_DE','2018-06-26 10:18:55','2018-06-26 10:18:55'), -(577,1,'FCS_FOODCOOPS_MAP_ENABLED','Auf Home Karte mit anderen Foodcoops anzeigen?','1','boolean',1280,'de_DE','2019-02-11 22:22:06','2019-02-11 22:22:06'), -(578,1,'FCS_WEEKLY_PICKUP_DAY','Wöchentlicher Abholtag','5','readonly',600,'de_DE','2019-02-18 12:38:10','2019-02-18 12:38:10'), -(579,1,'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA','Bestelllisten-Versand: x Tage vor dem Abholtag','2','readonly',650,'de_DE','2019-02-18 12:38:10','2019-02-18 12:38:10'), -(580,1,'FCS_ORDER_POSSIBLE_FOR_STOCK_PRODUCTS_IN_ORDERS_WITH_DELIVERY_RHYTHM','Sollen Lagerprodukte mit der wöchentlichen Bestellung bestellt werden können?','1','boolean',750,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(581,1,'FCS_SHOW_NON_STOCK_PRODUCTS_IN_INSTANT_ORDERS','In der Sofort-Bestellung ausschließlich Lagerprodukte anzeigen?','0','boolean',760,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(582,1,'FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES','Lagerprodukte in Rechnungen miteinbeziehen?','1','readonly',600,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(583,1,'FCS_REGISTRATION_NOTIFICATION_EMAILS','Wer soll bei neuen Registrierungen informiert werden?
Mehrere E-Mail-Adressen mit , (ohne Leerzeichen) trennen.
','fcs-demo-superadmin@mailinator.com','text',550,'de_DE','2019-03-05 20:08:00','2019-03-05 20:08:00'), -(584,1,'FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED','Selbstbedienungs-Modus für Lagerprodukte aktiv?
Zur Online-Doku
','0','boolean',3000,'de_DE','2019-04-17 20:01:59','2019-04-17 20:01:59'), -(585,1,'FCS_APP_ADDITIONAL_DATA','Zusätzliche Infos zur Foodcoop
Z.B. ZVR-Zahl
','','textarea',80,'de_DE','2019-08-03 20:07:17','2019-08-03 20:07:17'), -(586,1,'FCS_SELF_SERVICE_MODE_TEST_MODE_ENABLED','Selbstbedienungs-Modus im Test-Modus ausführen?
Keine Verlinkung im Haupt-Menü und bei Lagerprodukten.
','1','boolean',3100,'de_DE','2019-12-09 13:46:41','2019-12-09 13:46:41'), -(587,1,'FCS_CASHLESS_PAYMENT_ADD_TYPE','Art der Eintragung der Guthaben-Aufladungen
Wie gelangen die Guthaben-Aufladungen vom Bankkonto in den FoodCoopShop?
','manual','dropdown',1450,'de_DE','2020-02-11 10:13:10','2020-02-11 10:13:10'), -(589,1,'FCS_FEEDBACK_TO_PRODUCTS_ENABLED','Feedback-Funktion für Produkte aktiviert?
Mitglieder können Feedback zu bestellten Produkte verfassen.
','1','boolean',3200,'de_DE','2020-06-19 09:03:00','2020-06-19 09:03:00'), -(590,1,'FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY','Mitglied kann Abholtag beim Bestellen selbst auswählen.','0','readonly',590,'de_DE','2020-07-06 10:34:48','2020-07-06 10:34:48'), -(592,1,'FCS_SEND_INVOICES_TO_CUSTOMERS','Einzelhandels-Modus aktiviert?
Infos zur Verwendung im Einzelhandel
','0','readonly',580,'de_DE','2020-10-29 10:06:50','2020-10-29 10:06:50'), -(593,1,'FCS_DEPOSIT_TAX_RATE','Umsatzsteuersatz für Pfand','20,00','readonly',581,'de_DE','2020-11-03 15:24:12','2020-11-03 15:24:12'), -(594,1,'FCS_INVOICE_HEADER_TEXT','Header-Text für Rechnungen an Mitglieder','FoodCoop Test
Demostraße 4
A-4564 Demostadt
demo-foodcoop@maillinator.com','readonly',582,'de_DE','2020-11-03 15:24:12','2020-11-03 15:24:12'), -(595,1,'FCS_MEMBER_FEE_PRODUCTS','Welche Produkte werden als Mitgliedsbeitrag verwendet?
Die ausgewählten Produkte sind Datengrundlage der Spalte Mitgliedsbeitrag in der Mitgliederverwaltung und werden nicht in der Umsatzstatistik angezeigt.
','','multiple_dropdown',3300,'de_DE','2020-12-20 19:26:26','2020-12-20 19:26:26'), -(596,1,'FCS_CHECK_CREDIT_BALANCE_LIMIT','Ab welchem Guthaben-Stand soll die Erinnerungsmail versendet werden?','0','number',1450,'de_DE','2021-01-19 11:23:49','2021-01-19 11:23:49'), -(597,1,'FCS_PURCHASE_PRICE_ENABLED','Einkaufspreis für Produkte erfassen?
Der Einkaufspreis ist die Datengrundlage für die Gewinn-Statistik und für Lieferscheine an die Hersteller.
','0','readonly',587,'de_DE','2021-05-12 15:24:17','2021-05-12 15:24:17'), -(598,1,'FCS_HELLO_CASH_API_ENABLED','Schnittstelle (API) zu Registrierkasse HelloCash (hellocash.at) aktivieren?
Alle Rechnungen (bar und unbar) über die Registrierkasse erstellen.
','0','readonly',583,'de_DE','2021-07-07 10:55:14','2021-07-07 10:55:14'), -(599,1,'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS','Lagerort für Produkte erfassen und in Bestelllisten anzeigen?
Lagerorte: Keine Kühlung / Kühlschrank / Tiefkühler. Es erscheint ein zusätzlicher Button neben \"Bestellungen - Bestellungen als PDF generieren\"
','0','boolean',3210,'de_DE','2021-08-02 11:28:40','2021-08-02 11:28:40'), -(600,1,'FCS_INSTAGRAM_URL','Instagram-Url für die Einbindung im Footer','','text',920,'de_DE','2021-09-10 21:23:18','2021-09-10 21:23:18'), -(601,1,'FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY','Bestellungen beim ein- und zweiwöchigen Lieferhythmus sind nur in der Woche vor der Lieferung möglich.','0','boolean',3210,'de_DE','2022-02-01 17:48:46','2022-02-01 17:48:46'), -(602,1,'FCS_INVOICE_NUMBER_PREFIX','Präfix für Rechnungs-Nummernkreis
Max. 6 Zeichen inkl. Trennzeichen.
','','readonly',586,'de_DE','2022-03-21 12:03:07','2022-03-21 12:03:07'), -(603,1,'FCS_TAX_BASED_ON_NET_INVOICE_SUM','Rechnungslegung für pauschalierte Betriebe
Die Berechnung der Umsatzsteuer erfolgt auf Basis der Netto-Rechnungsumme und ist nicht die Summe der Umsatzsteuerbeträge pro Stück.
','0','readonly',585,'de_DE','2022-03-23 09:12:43','2022-03-23 09:12:43'), -(604,1,'FCS_NEWSLETTER_ENABLED','Newsletter-Funktion aktiv?
Mitglieder können sich bei der Registrierung für den Newsletter anmelden. Mehr Infos
','0','boolean',3400,'de_DE','2022-04-12 15:28:47','2022-04-12 15:28:47'), +INSERT INTO `fcs_configuration` VALUES +(11,1,'FCS_PRODUCT_AVAILABILITY_LOW','Geringe Verfügbarkeit
Ab welcher verfügbaren Produkt-Anzahl soll beim Bestellen der Hinweis \"(x verfügbar\") angezeigt werden?
','10','number',600,'de_DE','2017-07-26 13:19:19','2014-06-01 01:40:34'), +(31,1,'FCS_DAYS_SHOW_PRODUCT_AS_NEW','Wie viele Tage sollen Produkte \"als neu markiert\" bleiben?','7','number',700,'de_DE','2017-07-26 13:19:19','2014-05-14 21:15:45'), +(456,1,'FCS_FOOTER_CMS_TEXT','Zusätzlicher Text für den Footer',NULL,'textarea_big',920,'de_DE','2014-06-11 17:50:55','2016-07-01 21:47:47'), +(508,1,'FCS_FACEBOOK_URL','Facebook-Url für die Einbindung im Footer','https://www.facebook.com/FoodCoopShop/','text',910,'de_DE','2015-07-08 13:23:54','2015-07-08 13:23:54'), +(538,1,'FCS_REGISTRATION_EMAIL_TEXT','Zusätzlicher Text, der in der Bestätigungsmail nach einer Registrierung versendet wird.
E-Mail-Vorschau anzeigen','','textarea_big',1700,'de_DE','2016-06-26 00:00:00','2016-06-26 00:00:00'), +(543,1,'FCS_RIGHT_INFO_BOX_HTML','Inhalt der Box in der rechten Spalte unterhalb des Warenkorbes.
Um eine Zeile grün zu hinterlegen (Überschrift) bitte als \"Überschrift 3\" formatieren.
','

Abholzeiten

\r\n\r\n

Der Abholtag steht jetzt immer in der Produktbeschreibung, du kannst deine Produkte am Freitag abholen.

\r\n\r\n

Du kannst jede Woche bis spätestens Dienstag Mitternacht bestellen und sie am darauffolgenden Freitag abholen.

\r\n','textarea_big',1500,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), +(544,1,'FCS_NO_DELIVERY_DAYS_GLOBAL','Lieferpause für alle Hersteller?
Hier können lieferfreie Tage (z.B. Feiertage) für die gesamte Foodcoop festgelegt werden.
','','multiple_dropdown',100,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(545,1,'FCS_ACCOUNTING_EMAIL','E-Mail-Adresse des Finanzverantwortlichen
Wer bekommt die Benachrichtigung über den erfolgten Rechnungsversand?
','fcs-demo-superadmin@mailinator.com','text',1100,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(546,1,'FCS_REGISTRATION_INFO_TEXT','Info-Text beim Registrierungsformular
Beim Registrierungsformlar wird unterhalb der E-Mail-Adresse dieser Text angezeigt.
','Um bei uns zu bestellen musst du Vereinsmitglied sein.','textarea_big',1600,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(547,1,'FCS_SHOW_PRODUCTS_FOR_GUESTS','Produkte für nicht eingeloggte Mitglieder sichtbar?','0','boolean',200,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(548,1,'FCS_DEFAULT_NEW_MEMBER_ACTIVE','Neue Mitglieder automatisch aktivieren?','0','boolean',500,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(549,1,'FCS_MINIMAL_CREDIT_BALANCE','Bis zu welchem Guthaben-Betrag sollen Bestellungen möglich sein?','-100','number',1250,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(550,1,'FCS_BANK_ACCOUNT_DATA','Bankverbindung für die Guthaben-Einzahlungen\".','Guthaben-Konto Testbank / IBAN: AT65 5645 4154 8748 8999 / BIC: ABC87878','text',1300,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(552,1,'FCS_DELIVERY_DETAILS_FOR_MANUFACTURERS','Zusätzliche Liefer-Informationen für die Hersteller
wird in den Bestell-Listen nach dem Lieferdatum angezeigt.
',', 15:00 bis 17:00 Uhr','text',1200,'de_DE','2017-07-26 13:19:19','2017-07-26 13:19:19'), +(553,1,'FCS_BACKUP_EMAIL_ADDRESS_BCC','E-Mail-Adresse, an die sämtliche vom System generierten E-Mails als BCC verschickt werden (Backup).
Kann leer gelassen werden.
','','text',1900,'de_DE','2016-10-06 00:00:00','2016-10-06 00:00:00'), +(554,1,'FCS_SHOW_FOODCOOPSHOP_BACKLINK','Auf Homepage Link auf www.foodcoopshop.com anzeigen?
Der Link wird im Footer angezeigt.
','1','boolean',930,'de_DE','2016-11-27 00:00:00','2016-11-27 00:00:00'), +(556,1,'FCS_APP_NAME','Name der Foodcoop','FoodCoop Test','text',50,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(557,1,'FCS_APP_ADDRESS','Adresse der Foodcoop
Wird im Footer von Homepage und E-Mails, Datenschutzerklärung, Nutzungsbedingungen usw. verwendet.
','Demostraße 4
A-4564 Demostadt','textarea',60,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(558,1,'FCS_APP_EMAIL','E-Mail-Adresse der Foodcoop
','demo-foodcoop@maillinator.com','text',900,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(559,1,'FCS_PLATFORM_OWNER','Betreiber der Plattform
Für Datenschutzerklärung und Nutzungsbedingungen, bitte auch Adresse angeben. Kann leer gelassen werden, wenn die Foodcoop selbst die Plattform betreibt.
','','textarea',90,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), +(564,1,'FCS_ORDER_COMMENT_ENABLED','Kommentarfeld bei Bestell-Abschluss anzeigen?
Wird im Admin-Bereich unter \"Bestellungen\" angezeigt.
','1','boolean',130,'de_DE','2017-07-09 00:00:00','2017-07-09 00:00:00'), +(565,1,'FCS_USE_VARIABLE_MEMBER_FEE','Variablen Mitgliedsbeitrag verwenden?
Den variablen Mitgliedsbeitrag bei den Hersteller-Rechnungen abziehen? Die Produkt-Preise müssen entsprechend höher eingegeben werden.
','0','readonly',400,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), +(566,1,'FCS_DEFAULT_VARIABLE_MEMBER_FEE_PERCENTAGE','Standardwert für variablen Mitgliedsbeitrag
Der Prozentsatz kann in den Hersteller-Einstellungen auch individuell angepasst werden.
','0','readonly',500,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), +(567,1,'FCS_NETWORK_PLUGIN_ENABLED','Netzwerk-Modul aktiviert?
Infos zum Netzwerk-Modul
','1','readonly',500,'de_DE','2017-09-14 00:00:00','2017-09-14 00:00:00'), +(574,1,'FCS_SHOW_PRODUCT_PRICE_FOR_GUESTS','Produktpreis für nicht eingeloggte Mitglieder anzeigen?','0','boolean',210,'de_DE','2018-05-28 18:05:57','2018-05-28 18:05:57'), +(575,1,'FCS_CURRENCY_SYMBOL','Währungssymbol','€','readonly',520,'de_DE','2018-06-13 19:53:14','2018-06-13 19:53:14'), +(576,1,'FCS_DEFAULT_LOCALE','Sprache','de_DE','readonly',550,'de_DE','2018-06-26 10:18:55','2018-06-26 10:18:55'), +(577,1,'FCS_FOODCOOPS_MAP_ENABLED','Auf Home Karte mit anderen Foodcoops anzeigen?','1','boolean',1280,'de_DE','2019-02-11 22:22:06','2019-02-11 22:22:06'), +(578,1,'FCS_WEEKLY_PICKUP_DAY','Wöchentlicher Abholtag','5','readonly',600,'de_DE','2019-02-18 12:38:10','2019-02-18 12:38:10'), +(579,1,'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA','Bestelllisten-Versand: x Tage vor dem Abholtag','2','readonly',650,'de_DE','2019-02-18 12:38:10','2019-02-18 12:38:10'), +(580,1,'FCS_ORDER_POSSIBLE_FOR_STOCK_PRODUCTS_IN_ORDERS_WITH_DELIVERY_RHYTHM','Sollen Lagerprodukte mit der wöchentlichen Bestellung bestellt werden können?','1','boolean',750,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(581,1,'FCS_SHOW_NON_STOCK_PRODUCTS_IN_INSTANT_ORDERS','In der Sofort-Bestellung ausschließlich Lagerprodukte anzeigen?','0','boolean',760,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(582,1,'FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES','Lagerprodukte in Rechnungen miteinbeziehen?','1','readonly',600,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), +(583,1,'FCS_REGISTRATION_NOTIFICATION_EMAILS','Wer soll bei neuen Registrierungen informiert werden?
Mehrere E-Mail-Adressen mit , (ohne Leerzeichen) trennen.
','fcs-demo-superadmin@mailinator.com','text',550,'de_DE','2019-03-05 20:08:00','2019-03-05 20:08:00'), +(584,1,'FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED','Selbstbedienungs-Modus für Lagerprodukte aktiv?
Zur Online-Doku
','0','boolean',3000,'de_DE','2019-04-17 20:01:59','2019-04-17 20:01:59'), +(585,1,'FCS_APP_ADDITIONAL_DATA','Zusätzliche Infos zur Foodcoop
Z.B. ZVR-Zahl
','','textarea',80,'de_DE','2019-08-03 20:07:17','2019-08-03 20:07:17'), +(586,1,'FCS_SELF_SERVICE_MODE_TEST_MODE_ENABLED','Selbstbedienungs-Modus im Test-Modus ausführen?
Keine Verlinkung im Haupt-Menü und bei Lagerprodukten.
','1','boolean',3100,'de_DE','2019-12-09 13:46:41','2019-12-09 13:46:41'), +(587,1,'FCS_CASHLESS_PAYMENT_ADD_TYPE','Art der Eintragung der Guthaben-Aufladungen
Wie gelangen die Guthaben-Aufladungen vom Bankkonto in den FoodCoopShop?
','manual','dropdown',1450,'de_DE','2020-02-11 10:13:10','2020-02-11 10:13:10'), +(589,1,'FCS_FEEDBACK_TO_PRODUCTS_ENABLED','Feedback-Funktion für Produkte aktiviert?
Mitglieder können Feedback zu bestellten Produkte verfassen.
','1','boolean',3200,'de_DE','2020-06-19 09:03:00','2020-06-19 09:03:00'), +(590,1,'FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY','Mitglied kann Abholtag beim Bestellen selbst auswählen.','0','readonly',590,'de_DE','2020-07-06 10:34:48','2020-07-06 10:34:48'), +(592,1,'FCS_SEND_INVOICES_TO_CUSTOMERS','Einzelhandels-Modus aktiviert?
Infos zur Verwendung im Einzelhandel
','0','readonly',580,'de_DE','2020-10-29 10:06:50','2020-10-29 10:06:50'), +(593,1,'FCS_DEPOSIT_TAX_RATE','Umsatzsteuersatz für Pfand','20,00','readonly',581,'de_DE','2020-11-03 15:24:12','2020-11-03 15:24:12'), +(594,1,'FCS_INVOICE_HEADER_TEXT','Header-Text für Rechnungen an Mitglieder','FoodCoop Test
Demostraße 4
A-4564 Demostadt
demo-foodcoop@maillinator.com','readonly',582,'de_DE','2020-11-03 15:24:12','2020-11-03 15:24:12'), +(595,1,'FCS_MEMBER_FEE_PRODUCTS','Welche Produkte werden als Mitgliedsbeitrag verwendet?
Die ausgewählten Produkte sind Datengrundlage der Spalte Mitgliedsbeitrag in der Mitgliederverwaltung und werden nicht in der Umsatzstatistik angezeigt.
','','multiple_dropdown',3300,'de_DE','2020-12-20 19:26:26','2020-12-20 19:26:26'), +(596,1,'FCS_CHECK_CREDIT_BALANCE_LIMIT','Ab welchem Guthaben-Stand soll die Erinnerungsmail versendet werden?','0','number',1450,'de_DE','2021-01-19 11:23:49','2021-01-19 11:23:49'), +(597,1,'FCS_PURCHASE_PRICE_ENABLED','Einkaufspreis für Produkte erfassen?
Der Einkaufspreis ist die Datengrundlage für die Gewinn-Statistik und für Lieferscheine an die Hersteller.
','0','readonly',587,'de_DE','2021-05-12 15:24:17','2021-05-12 15:24:17'), +(598,1,'FCS_HELLO_CASH_API_ENABLED','Schnittstelle (API) zu Registrierkasse HelloCash (hellocash.at) aktivieren?
Alle Rechnungen (bar und unbar) über die Registrierkasse erstellen.
','0','readonly',583,'de_DE','2021-07-07 10:55:14','2021-07-07 10:55:14'), +(599,1,'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS','Lagerort für Produkte erfassen und in Bestelllisten anzeigen?
Lagerorte: Keine Kühlung / Kühlschrank / Tiefkühler. Es erscheint ein zusätzlicher Button neben \"Bestellungen - Bestellungen als PDF generieren\"
','0','boolean',3210,'de_DE','2021-08-02 11:28:40','2021-08-02 11:28:40'), +(600,1,'FCS_INSTAGRAM_URL','Instagram-Url für die Einbindung im Footer','','text',920,'de_DE','2021-09-10 21:23:18','2021-09-10 21:23:18'), +(601,1,'FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY','Bestellungen beim ein- und zweiwöchigen Lieferhythmus sind nur in der Woche vor der Lieferung möglich.','0','boolean',3210,'de_DE','2022-02-01 17:48:46','2022-02-01 17:48:46'), +(602,1,'FCS_INVOICE_NUMBER_PREFIX','Präfix für Rechnungs-Nummernkreis
Max. 6 Zeichen inkl. Trennzeichen.
','','readonly',586,'de_DE','2022-03-21 12:03:07','2022-03-21 12:03:07'), +(603,1,'FCS_TAX_BASED_ON_NET_INVOICE_SUM','Rechnungslegung für pauschalierte Betriebe
Die Berechnung der Umsatzsteuer erfolgt auf Basis der Netto-Rechnungsumme und ist nicht die Summe der Umsatzsteuerbeträge pro Stück.
','0','readonly',585,'de_DE','2022-03-23 09:12:43','2022-03-23 09:12:43'), +(604,1,'FCS_NEWSLETTER_ENABLED','Newsletter-Funktion aktiv?
Mitglieder können sich bei der Registrierung für den Newsletter anmelden. Mehr Infos
','0','boolean',3400,'de_DE','2022-04-12 15:28:47','2022-04-12 15:28:47'), (605,1,'FCS_USER_FEEDBACK_ENABLED','Mitglieder- und Hersteller-Feedback aktiv?
Ermöglicht das Erfassen und Anzeigen von Feedback. Mehr Infos
','0','boolean',3500,'de_DE','2022-07-19 14:39:45','2022-07-19 14:39:45'); /*!40000 ALTER TABLE `fcs_configuration` ENABLE KEYS */; @@ -192,27 +201,27 @@ /*!40000 ALTER TABLE `fcs_cronjob_logs` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_cronjobs` DISABLE KEYS */; -INSERT INTO `fcs_cronjobs` VALUES -(1,'TestCronjob','day',NULL,NULL,'22:30:00',1), -(2,'TestCronjob','week',NULL,'Monday','09:00:00',1), +INSERT INTO `fcs_cronjobs` VALUES +(1,'TestCronjob','day',NULL,NULL,'22:30:00',1), +(2,'TestCronjob','week',NULL,'Monday','09:00:00',1), (3,'TestCronjob','month',11,NULL,'07:30:00',1); /*!40000 ALTER TABLE `fcs_cronjobs` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_customer` DISABLE KEYS */; -INSERT INTO `fcs_customer` VALUES -(87,3,0,'Demo','Mitglied','fcs-demo-mitglied@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,1,'2018-08-03',NULL,1,'2014-12-02 12:19:31','2015-12-06 23:37:44',0,0,'SP',1,1,1,1,0), -(88,4,0,'Demo','Admin','fcs-demo-admin@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,1,'2018-08-03',NULL,1,'2014-12-02 12:28:43','2016-09-29 16:25:09',0,0,'SP',1,1,1,1,0), -(89,4,0,'Demo','Gemüse-Hersteller','fcs-demo-gemuese-hersteller@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,1,'2014-12-02 12:37:26','2015-03-11 18:12:10',0,0,'SP',1,1,1,1,0), -(90,4,0,'Demo','Milch-Hersteller','fcs-demo-milch-hersteller@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,1,'2014-12-02 12:37:49','2015-03-11 18:11:54',0,0,'SP',1,1,1,1,0), -(91,4,0,'Demo','Fleisch-Hersteller','fcs-demo-fleisch-hersteller@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,1,'2014-12-02 12:38:12','2015-03-11 18:11:47',0,0,'SP',1,1,1,1,0), -(92,5,0,'Demo','Superadmin','fcs-demo-superadmin@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,1,'2018-08-03',NULL,1,'2016-09-29 16:26:12','2016-09-29 16:26:12',0,0,'SP',1,1,1,1,0), -(93,2,0,'Demo','SB-Kunde','fcs-demo-sb-kunde@mailinator.com','$2y$10$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,0,'2016-09-29 16:26:12','2016-09-29 16:26:12',0,0,'SP',1,1,1,1,0); +INSERT INTO `fcs_customer` VALUES +(87,3,0,'Demo','Mitglied','fcs-demo-mitglied@mailinator.com','$2y$10\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,1,'2018-08-03',NULL,1,'2014-12-02 12:19:31','2015-12-06 23:37:44',0,0,'SP',1,1,1,1,0), +(88,4,0,'Demo','Admin','fcs-demo-admin@mailinator.com','$2y$10\\\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,1,'2018-08-03',NULL,1,'2014-12-02 12:28:43','2016-09-29 16:25:09',0,0,'SP',1,1,1,1,0), +(89,4,0,'Demo','Gemüse-Hersteller','fcs-demo-gemuese-hersteller@mailinator.com','$2y$10\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,1,'2014-12-02 12:37:26','2015-03-11 18:12:10',0,0,'SP',1,1,1,1,0), +(90,4,0,'Demo','Milch-Hersteller','fcs-demo-milch-hersteller@mailinator.com','$2y$10\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,1,'2014-12-02 12:37:49','2015-03-11 18:11:54',0,0,'SP',1,1,1,1,0), +(91,4,0,'Demo','Fleisch-Hersteller','fcs-demo-fleisch-hersteller@mailinator.com','$2y$10\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,1,'2014-12-02 12:38:12','2015-03-11 18:11:47',0,0,'SP',1,1,1,1,0), +(92,5,0,'Demo','Superadmin','fcs-demo-superadmin@mailinator.com','$2y$10\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,1,'2018-08-03',NULL,1,'2016-09-29 16:26:12','2016-09-29 16:26:12',0,0,'SP',1,1,1,1,0), +(93,2,0,'Demo','SB-Kunde','fcs-demo-sb-kunde@mailinator.com','$2y$10\$uu/znwy2GwCx0NlLOIqaquY862AdcV6BgTGtNEUdKj4o1US.idVlm',NULL,NULL,NULL,0,'2018-08-03',NULL,0,'2016-09-29 16:26:12','2016-09-29 16:26:12',0,0,'SP',1,1,1,1,0); /*!40000 ALTER TABLE `fcs_customer` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_deposits` DISABLE KEYS */; -INSERT INTO `fcs_deposits` VALUES -(1,346,0,0.5), -(2,0,9,0.5), +INSERT INTO `fcs_deposits` VALUES +(1,346,0,0.5), +(2,0,9,0.5), (3,0,10,0.5); /*!40000 ALTER TABLE `fcs_deposits` ENABLE KEYS */; @@ -220,9 +229,9 @@ /*!40000 ALTER TABLE `fcs_feedbacks` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_images` DISABLE KEYS */; -INSERT INTO `fcs_images` VALUES -(154,60), -(156,340), +INSERT INTO `fcs_images` VALUES +(154,60), +(156,340), (157,338); /*!40000 ALTER TABLE `fcs_images` ENABLE KEYS */; @@ -233,17 +242,17 @@ /*!40000 ALTER TABLE `fcs_invoices` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_manufacturer` DISABLE KEYS */; -INSERT INTO `fcs_manufacturer` VALUES -(4,'Demo Fleisch-Hersteller','

tests

\r\n','','2014-05-14 13:23:02','2015-05-15 13:31:41',1,0,'','','','','','','','','','',NULL,0,1,1,2,NULL,'testfcs1@mailinator.com,testfcs2@mailinator.com',NULL,NULL,NULL,1,NULL,0,0,0,'',1,0), -(5,'Demo Gemüse-Hersteller','

Gemüse-Hersteller Beschreibung lang

','
\r\n

Gemüse-Hersteller Beschreibung kurz

\r\n
','2014-05-14 13:36:44','2016-09-27 09:34:51',1,0,'','','','','','','','','','',88,0,1,1,1,NULL,'',NULL,NULL,NULL,NULL,'1',1,1,1,'',1,0), -(15,'Demo Milch-Hersteller','

Ja, ich bin der Milchhersteller!

','','2014-06-04 21:45:12','2016-03-07 09:02:25',1,0,'','','','','','','','','','',NULL,0,1,1,4,NULL,'test@test.at',NULL,NULL,NULL,NULL,NULL,0,0,0,'',1,1), -(16,'Hersteller ohne Customer-Eintrag','','','2014-06-04 21:45:12','2016-03-07 09:02:25',1,0,'','','','','','','','','','',NULL,10,1,1,1,NULL,'',NULL,NULL,NULL,NULL,NULL,0,0,0,'',1,0); +INSERT INTO `fcs_manufacturer` VALUES +(4,'Demo Fleisch-Hersteller','

tests

\r\n','','2014-05-14 13:23:02','2015-05-15 13:31:41',1,0,'','','','','','','','','','',NULL,0,1,1,2,NULL,'testfcs1@mailinator.com,testfcs2@mailinator.com',NULL,NULL,NULL,1,NULL,0,0,0,'',1,0,0), +(5,'Demo Gemüse-Hersteller','

Gemüse-Hersteller Beschreibung lang

','
\r\n

Gemüse-Hersteller Beschreibung kurz

\r\n
','2014-05-14 13:36:44','2016-09-27 09:34:51',1,0,'','','','','','','','','','',88,0,1,1,1,NULL,'',NULL,NULL,NULL,NULL,'1',1,1,1,'',1,0,0), +(15,'Demo Milch-Hersteller','

Ja, ich bin der Milchhersteller!

','','2014-06-04 21:45:12','2016-03-07 09:02:25',1,0,'','','','','','','','','','',NULL,0,1,1,4,NULL,'test@test.at',NULL,NULL,NULL,NULL,NULL,0,0,0,'',1,1,0), +(16,'Hersteller ohne Customer-Eintrag','','','2014-06-04 21:45:12','2016-03-07 09:02:25',1,0,'','','','','','','','','','',NULL,10,1,1,1,NULL,'',NULL,NULL,NULL,NULL,NULL,0,0,0,'',1,0,0); /*!40000 ALTER TABLE `fcs_manufacturer` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_order_detail` DISABLE KEYS */; -INSERT INTO `fcs_order_detail` VALUES -(1,346,0,'Artischocke : Stück',1,1.820000,1.650000,0.170000,0.170000,10.000,0.50,92,NULL,1,3,'2018-02-02','SP','2018-02-01 09:17:14','2021-05-04 11:10:14'), -(2,340,0,'Beuschl',1,4.540000,4.540000,0.000000,0.000000,0.000,0.00,92,NULL,2,3,'2018-02-02','SP','2018-02-01 09:17:14','2021-05-04 11:10:14'), +INSERT INTO `fcs_order_detail` VALUES +(1,346,0,'Artischocke : Stück',1,1.820000,1.650000,0.170000,0.170000,10.000,0.50,92,NULL,1,3,'2018-02-02','SP','2018-02-01 09:17:14','2021-05-04 11:10:14'), +(2,340,0,'Beuschl',1,4.540000,4.540000,0.000000,0.000000,0.000,0.00,92,NULL,2,3,'2018-02-02','SP','2018-02-01 09:17:14','2021-05-04 11:10:14'), (3,60,10,'Milch : 0,5l',1,0.620000,0.550000,0.070000,0.070000,13.000,0.50,92,NULL,3,3,'2018-02-02','SP','2018-02-01 09:17:14','2021-05-04 11:10:14'); /*!40000 ALTER TABLE `fcs_order_detail` ENABLE KEYS */; @@ -257,13 +266,13 @@ /*!40000 ALTER TABLE `fcs_order_detail_units` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_pages` DISABLE KEYS */; -INSERT INTO `fcs_pages` VALUES +INSERT INTO `fcs_pages` VALUES (3,'Page','',1,'header',1,'',88,0,'2016-08-29 13:36:43','2016-08-29 13:36:43',0,0,0,0); /*!40000 ALTER TABLE `fcs_pages` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_payments` DISABLE KEYS */; -INSERT INTO `fcs_payments` VALUES -(1,92,0,'product',100.00,'','2018-07-03 20:00:20','2018-07-03 20:00:20',NULL,NULL,0,1,0,'',0,92), +INSERT INTO `fcs_payments` VALUES +(1,92,0,'product',100.00,'','2018-07-03 20:00:20','2018-07-03 20:00:20',NULL,NULL,0,1,0,'',0,92), (2,87,0,'product',100000.00,'','2020-12-09 20:00:20','2020-12-09 20:00:20',NULL,NULL,0,1,0,'',0,87); /*!40000 ALTER TABLE `fcs_payments` ENABLE KEYS */; @@ -271,190 +280,121 @@ /*!40000 ALTER TABLE `fcs_pickup_days` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_product` DISABLE KEYS */; -INSERT INTO `fcs_product` VALUES -(60,15,3,1,0.909091,'Milch','','1 Liter','',0,0,1,'week',1,NULL,NULL,3,NULL,'2014-06-11 21:20:24','2014-12-14 19:47:33'), -(102,4,2,1,0.000000,'Frankfurter','','

2 Paar

','',0,0,1,'week',1,NULL,NULL,3,NULL,'2016-04-27 21:13:37','2014-09-19 14:32:51'), -(103,4,2,1,3.181819,'Bratwürstel','','2 Paar','',0,0,1,'week',1,NULL,NULL,3,NULL,'2016-05-05 08:28:49','2014-08-16 14:05:58'), -(163,5,0,1,1.363637,'Mangold','','0,25kg','',0,0,1,'week',1,NULL,NULL,3,NULL,'2014-07-12 20:41:43','2017-07-26 13:24:10'), -(339,5,0,1,0.000000,'Kartoffel','','','',0,0,1,'week',1,NULL,NULL,3,NULL,'2015-09-07 12:05:38','2015-02-26 13:54:07'), -(340,4,0,1,4.545455,'Beuschl','','','',0,0,1,'week',1,NULL,NULL,3,NULL,'2016-05-05 08:28:45','2015-06-23 14:52:53'), -(344,5,0,1,0.636364,'Knoblauch','','','100 g',0,0,1,'week',1,NULL,NULL,3,NULL,'2015-10-05 17:22:40','2015-07-06 10:24:44'), -(346,5,2,1,1.652893,'Artischocke','','','Stück',0,0,1,'week',1,NULL,NULL,3,NULL,'2015-08-19 09:35:46','2015-08-19 09:35:45'), -(347,4,2,1,0.000000,'Forelle','','','Stück',0,0,1,'week',1,NULL,NULL,3,NULL,'2018-05-17 16:13:39','2018-05-17 16:15:21'), -(348,4,2,1,0.000000,'Rindfleisch','','','',0,0,1,'week',1,NULL,NULL,3,NULL,'2018-05-17 16:15:33','2018-05-17 16:16:38'), -(349,5,2,1,4.545455,'Lagerprodukt','','','',0,1,1,'week',1,NULL,NULL,3,NULL,'2018-08-16 12:15:48','2018-08-16 12:16:51'), -(350,5,2,1,0.000000,'Lagerprodukt mit Varianten','','','',0,1,1,'week',1,NULL,NULL,3,NULL,'2018-08-16 12:19:06','2018-08-16 12:19:23'), +INSERT INTO `fcs_product` VALUES +(60,15,3,1,0.909091,'Milch','','1 Liter','',0,0,1,'week',1,NULL,NULL,3,NULL,'2014-06-11 21:20:24','2014-12-14 19:47:33'), +(102,4,2,1,0.000000,'Frankfurter','','

2 Paar

','',0,0,1,'week',1,NULL,NULL,3,NULL,'2016-04-27 21:13:37','2014-09-19 14:32:51'), +(103,4,2,1,3.181819,'Bratwürstel','','2 Paar','',0,0,1,'week',1,NULL,NULL,3,NULL,'2016-05-05 08:28:49','2014-08-16 14:05:58'), +(163,5,0,1,1.363637,'Mangold','','0,25kg','',0,0,1,'week',1,NULL,NULL,3,NULL,'2014-07-12 20:41:43','2017-07-26 13:24:10'), +(339,5,0,1,0.000000,'Kartoffel','','','',0,0,1,'week',1,NULL,NULL,3,NULL,'2015-09-07 12:05:38','2015-02-26 13:54:07'), +(340,4,0,1,4.545455,'Beuschl','','','',0,0,1,'week',1,NULL,NULL,3,NULL,'2016-05-05 08:28:45','2015-06-23 14:52:53'), +(344,5,0,1,0.636364,'Knoblauch','','','100 g',0,0,1,'week',1,NULL,NULL,3,NULL,'2015-10-05 17:22:40','2015-07-06 10:24:44'), +(346,5,2,1,1.652893,'Artischocke','','','Stück',0,0,1,'week',1,NULL,NULL,3,NULL,'2015-08-19 09:35:46','2015-08-19 09:35:45'), +(347,4,2,1,0.000000,'Forelle','','','Stück',0,0,1,'week',1,NULL,NULL,3,NULL,'2018-05-17 16:13:39','2018-05-17 16:15:21'), +(348,4,2,1,0.000000,'Rindfleisch','','','',0,0,1,'week',1,NULL,NULL,3,NULL,'2018-05-17 16:15:33','2018-05-17 16:16:38'), +(349,5,2,1,4.545455,'Lagerprodukt','','','',0,1,1,'week',1,NULL,NULL,3,NULL,'2018-08-16 12:15:48','2018-08-16 12:16:51'), +(350,5,2,1,0.000000,'Lagerprodukt mit Varianten','','','',0,1,1,'week',1,NULL,NULL,3,NULL,'2018-08-16 12:19:06','2018-08-16 12:19:23'), (351,5,1,1,0.000000,'Lagerprodukt 2','','','',0,1,1,'week',1,NULL,NULL,3,NULL,'2019-06-05 15:09:53','2019-06-05 15:10:08'); /*!40000 ALTER TABLE `fcs_product` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_product_attribute` DISABLE KEYS */; -INSERT INTO `fcs_product_attribute` VALUES -(10,60,0.545455,0), -(11,348,0.000000,1), -(12,348,0.000000,0), -(13,350,1.818182,1), -(14,350,3.636364,0), +INSERT INTO `fcs_product_attribute` VALUES +(10,60,0.545455,0), +(11,348,0.000000,1), +(12,348,0.000000,0), +(13,350,1.818182,1), +(14,350,3.636364,0), (15,350,0.000000,0); /*!40000 ALTER TABLE `fcs_product_attribute` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_product_attribute_combination` DISABLE KEYS */; -INSERT INTO `fcs_product_attribute_combination` VALUES -(33,10), -(36,11), -(35,12), -(36,13), -(35,14), +INSERT INTO `fcs_product_attribute_combination` VALUES +(33,10), +(36,11), +(35,12), +(36,13), +(35,14), (36,15); /*!40000 ALTER TABLE `fcs_product_attribute_combination` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_purchase_prices` DISABLE KEYS */; -INSERT INTO `fcs_purchase_prices` VALUES -(1,346,0,1,1.200000), -(2,0,13,0,1.400000), -(3,347,0,3,NULL), -(4,348,0,3,NULL), -(5,60,0,2,NULL), -(6,0,10,0,0.250000), +INSERT INTO `fcs_purchase_prices` VALUES +(1,346,0,1,1.200000), +(2,0,13,0,1.400000), +(3,347,0,3,NULL), +(4,348,0,3,NULL), +(5,60,0,2,NULL), +(6,0,10,0,0.250000), (7,163,0,0,1.072727); /*!40000 ALTER TABLE `fcs_purchase_prices` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_sliders` DISABLE KEYS */; -INSERT INTO `fcs_sliders` VALUES -(6,'2be64c60e6126c9085fd9d9717532a14e5a5bb4e_slide4.png',NULL,0,0,1); +INSERT INTO `fcs_sliders` VALUES +(6,'demo-slider.jpg',NULL,0,0,1); /*!40000 ALTER TABLE `fcs_sliders` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_stock_available` DISABLE KEYS */; -INSERT INTO `fcs_stock_available` VALUES -(132,60,0,1015,0,NULL,1,NULL), -(195,102,0,2996,0,NULL,1,NULL), -(196,103,0,990,0,NULL,1,NULL), -(318,163,0,988,0,NULL,1,NULL), -(674,339,0,2959,0,NULL,1,NULL), -(678,340,0,990,0,NULL,1,NULL), -(680,344,0,78,0,NULL,0,NULL), -(686,346,0,97,0,NULL,0,NULL), -(692,60,9,996,0,NULL,1,NULL), -(693,60,10,19,0,NULL,0,NULL), -(704,347,0,999,0,NULL,1,NULL), -(705,348,0,1998,0,NULL,1,NULL), -(706,348,11,999,0,NULL,1,NULL), -(707,348,12,999,0,NULL,1,NULL), -(708,349,0,5,-5,0,0,NULL), -(709,350,0,1004,0,NULL,1,NULL), -(710,350,13,5,-5,0,0,NULL), -(711,350,14,999,0,NULL,1,NULL), -(712,350,15,999,0,NULL,1,NULL), +INSERT INTO `fcs_stock_available` VALUES +(132,60,0,1015,0,NULL,1,NULL), +(195,102,0,2996,0,NULL,1,NULL), +(196,103,0,990,0,NULL,1,NULL), +(318,163,0,988,0,NULL,1,NULL), +(674,339,0,2959,0,NULL,1,NULL), +(678,340,0,990,0,NULL,1,NULL), +(680,344,0,78,0,NULL,0,NULL), +(686,346,0,97,0,NULL,0,NULL), +(692,60,9,996,0,NULL,1,NULL), +(693,60,10,19,0,NULL,0,NULL), +(704,347,0,999,0,NULL,1,NULL), +(705,348,0,1998,0,NULL,1,NULL), +(706,348,11,999,0,NULL,1,NULL), +(707,348,12,999,0,NULL,1,NULL), +(708,349,0,5,-5,0,0,NULL), +(709,350,0,1004,0,NULL,1,NULL), +(710,350,13,5,-5,0,0,NULL), +(711,350,14,999,0,NULL,1,NULL), +(712,350,15,999,0,NULL,1,NULL), (713,351,0,999,0,NULL,1,NULL); /*!40000 ALTER TABLE `fcs_stock_available` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_storage_locations` DISABLE KEYS */; -INSERT INTO `fcs_storage_locations` VALUES -(1,'Keine Kühlung',10), -(2,'Kühlschrank',20), +INSERT INTO `fcs_storage_locations` VALUES +(1,'Keine Kühlung',10), +(2,'Kühlschrank',20), (3,'Tiefkühler',30); /*!40000 ALTER TABLE `fcs_storage_locations` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_sync_domains` DISABLE KEYS */; -INSERT INTO `fcs_sync_domains` VALUES +INSERT INTO `fcs_sync_domains` VALUES (1,'{{serverName}}',1); /*!40000 ALTER TABLE `fcs_sync_domains` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_sync_products` DISABLE KEYS */; -INSERT INTO `fcs_sync_products` VALUES -(1,1,346,346,0,0), -(2,1,350,350,0,0), -(3,1,350,350,14,14), +INSERT INTO `fcs_sync_products` VALUES +(1,1,346,346,0,0), +(2,1,350,350,0,0), +(3,1,350,350,14,14), (4,1,350,350,13,13); /*!40000 ALTER TABLE `fcs_sync_products` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_tax` DISABLE KEYS */; -INSERT INTO `fcs_tax` VALUES -(1,20.000,1,0), -(2,10.000,1,0), +INSERT INTO `fcs_tax` VALUES +(1,20.000,1,0), +(2,10.000,1,0), (3,13.000,1,0); /*!40000 ALTER TABLE `fcs_tax` ENABLE KEYS */; /*!40000 ALTER TABLE `fcs_units` DISABLE KEYS */; -INSERT INTO `fcs_units` VALUES -(1,347,0,1.50,0.98,'g',100,1,350.000), -(2,0,11,20.00,NULL,'kg',1,1,0.500), -(3,0,12,20.00,14.00,'g',500,1,300.000), -(4,349,0,0.00,NULL,'kg',1,0,0.000), -(5,0,13,0.00,NULL,'kg',1,0,0.000), -(6,0,14,0.00,NULL,'kg',1,0,0.000), -(7,0,15,10.00,6.00,'kg',1,1,0.500), +INSERT INTO `fcs_units` VALUES +(1,347,0,1.50,0.98,'g',100,1,350.000), +(2,0,11,20.00,NULL,'kg',1,1,0.500), +(3,0,12,20.00,14.00,'g',500,1,300.000), +(4,349,0,0.00,NULL,'kg',1,0,0.000), +(5,0,13,0.00,NULL,'kg',1,0,0.000), +(6,0,14,0.00,NULL,'kg',1,0,0.000), +(7,0,15,10.00,6.00,'kg',1,1,0.500), (8,351,0,15.00,NULL,'kg',1,1,1.000); /*!40000 ALTER TABLE `fcs_units` ENABLE KEYS */; -/*!40000 ALTER TABLE `phinxlog` DISABLE KEYS */; -INSERT INTO `phinxlog` VALUES -(20200404145856,'RemoveV2Migrations','2020-04-04 15:01:12','2020-04-04 15:01:12',0), -(20200415073329,'ShowNewProductsOnHome','2020-04-15 07:42:02','2020-04-15 07:42:02',0), -(20200501192722,'EnableCashlessPaymentAddTypeConfiguration','2020-05-01 19:30:17','2020-05-01 19:30:17',0), -(20200618063024,'AddProductFeedback','2020-06-19 07:02:54','2020-06-19 07:02:55',0), -(20200703072605,'CustomerCanSelectPickupDay','2020-07-06 08:34:44','2020-07-06 08:34:44',0), -(20200831142250,'RemoveEmailLogTable','2020-08-31 15:10:29','2020-08-31 15:10:29',0), -(20200910091755,'AddMemberSettingUseCameraForMobileBarcodeScanning','2020-09-10 09:21:00','2020-09-10 09:21:00',0), -(20200925073919,'GermanIbanFix','2020-09-25 08:12:53','2020-09-25 08:12:53',0), -(20201017182431,'AdaptMinimalCreditBalance','2020-10-17 18:38:11','2020-10-17 18:38:11',0), -(20201029084931,'AddRetailMode','2020-10-29 09:06:50','2020-10-29 09:06:50',0), -(20201029084932,'AddRetailMode','2020-11-09 10:31:46','2020-11-09 10:31:47',0), -(20201118084516,'AddRetailMode2','2020-11-18 08:47:48','2020-11-18 08:47:48',0), -(20201213120713,'AddRetailMode3','2020-12-13 12:14:11','2020-12-13 12:14:11',0), -(20201217101514,'SliderWithLink','2020-12-17 10:26:47','2020-12-17 10:26:47',0), -(20201217101515,'SliderWithLink','2020-12-17 18:47:08','2020-12-17 18:47:08',0), -(20201220182015,'ImproveMemberFeeAdministration','2020-12-20 18:26:26','2020-12-20 18:26:26',0), -(20210119101923,'CheckCreditBalanceLimit','2021-01-19 10:23:49','2021-01-19 10:23:49',0), -(20210401071718,'RemoveCustomerGroupSetting','2021-04-01 07:18:55','2021-04-01 07:18:55',0), -(20210401082727,'CustomerActivateEmailCode','2021-04-01 08:29:23','2021-04-01 08:29:23',0), -(20210419084816,'BlogPostShowOnStartPageUntilDate','2021-04-19 09:41:23','2021-04-19 09:41:24',0), -(20210427144234,'RemoveOldMemberFeeSetting','2021-04-27 15:05:04','2021-04-27 15:05:04',0), -(20210504085123,'SaveTaxInOrderDetails','2021-05-04 09:10:14','2021-05-04 09:10:14',0), -(20210510080630,'EnablePurchasePrices','2021-05-12 13:24:17','2021-05-12 13:24:18',0), -(20210707083827,'AddRegistrierkasseApi','2021-07-07 08:55:14','2021-07-07 08:55:14',0), -(20210802090623,'AddStorageLocation','2021-08-02 09:28:40','2021-08-02 09:28:40',0), -(20210910191430,'Instagram','2021-09-10 19:23:18','2021-09-10 19:23:18',0), -(20210914071747,'DifferentPricesForCustomers','2021-09-16 05:50:12','2021-09-16 05:50:12',0), -(20210922154148,'RemoveUnusedQueueTable','2021-09-22 15:43:09','2021-09-22 15:43:09',0), -(20210923073422,'RemoveSettingShowNewProductsOnHome','2021-09-23 07:39:32','2021-09-23 07:39:32',0), -(20210923090820,'AllowNullAsPurchasePrice','2021-09-23 09:09:52','2021-09-23 09:09:52',0), -(20211028083847,'UseExistingBarcode','2021-10-28 08:45:02','2021-10-28 08:45:02',0), -(20211123095227,'DeactivateCheckCreditReminder','2021-11-23 10:01:05','2021-11-23 10:01:05',0), -(20211213081433,'ImproveCustomerNotifications','2021-12-13 09:24:45','2021-12-13 09:24:46',0), -(20211213081434,'ImproveCustomerNotifications','2021-12-13 10:44:55','2021-12-13 10:44:55',0), -(20211215184633,'ManufacturerSettingIncludeStockProductsOnOrderList','2021-12-15 18:51:00','2021-12-15 18:51:00',0), -(20211229194617,'AddIndizesForBetterPerformance','2021-12-29 19:53:31','2021-12-29 19:53:31',0), -(20220129082136,'SendDeliveryNotesEveryMonth','2022-01-29 08:32:01','2022-01-29 08:32:02',0), -(20220201163254,'OptionalDeliveryRhythmSettingOrderInWeekBeforeDelivery','2022-02-01 16:48:46','2022-02-01 16:48:46',0), -(20220321103059,'PrefixForInvoices','2022-03-21 11:03:07','2022-03-21 11:03:07',0), -(20220323075926,'TaxBasedOnNetInvoiceSum','2022-03-23 08:12:43','2022-03-23 08:12:43',0), -(20220407093247,'AddIsCompanyFieldForCustomer','2022-04-07 09:37:45','2022-04-07 09:37:45',0), -(20220412131842,'Newsletter','2022-04-12 13:29:05','2022-04-12 13:29:05',0), -(20220525092822,'BiggerQueuedJobDataField','2022-05-25 09:32:10','2022-05-25 09:32:10',0), -(20220620091755,'RemoveTimebasedCurrencyModule','2022-06-20 09:30:39','2022-06-20 09:30:39',0), -(20220717194215,'UserFeedback','2022-07-19 12:39:45','2022-07-19 12:39:45',0); -/*!40000 ALTER TABLE `phinxlog` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queue_phinxlog` DISABLE KEYS */; -INSERT INTO `queue_phinxlog` VALUES -(20150425180802,'Init','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20150511062806,'Fixmissing','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20150911132343,'ImprovementsForMysql','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20161319000000,'IncreaseDataSize','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20161319000001,'Priority','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20161319000002,'Rename','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20161319000003,'Processes','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20171013131845,'AlterQueuedJobs','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20171013133145,'Utf8mb4Fix','2020-09-17 07:23:31','2020-09-17 07:23:31',0), -(20171019083500,'ColumnLength','2020-09-17 07:23:31','2020-09-17 07:23:32',0), -(20171019083501,'MigrationQueueNull','2020-09-17 07:23:32','2020-09-17 07:23:32',0), -(20171019083502,'MigrationQueueStatus','2020-09-17 07:23:32','2020-09-17 07:23:32',0), -(20171019083503,'MigrationQueueProcesses','2020-09-17 07:23:32','2020-09-17 07:23:32',0), -(20171019083505,'MigrationQueueProcessesIndex','2020-09-17 07:23:32','2020-09-17 07:23:32',0), -(20171019083506,'MigrationQueueProcessesKey','2020-09-17 07:23:32','2020-09-17 07:23:32',0), -(20191319000002,'MigrationQueueRename','2021-07-20 11:13:02','2021-07-20 11:13:02',0); -/*!40000 ALTER TABLE `queue_phinxlog` ENABLE KEYS */; - /*!40000 ALTER TABLE `queue_processes` DISABLE KEYS */; /*!40000 ALTER TABLE `queue_processes` ENABLE KEYS */; @@ -470,3 +410,7 @@ /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +"; + $this->execute($query); + } +} diff --git a/config/app.php b/config/app.php index 335b06df09..7340ab7521 100644 --- a/config/app.php +++ b/config/app.php @@ -1,4 +1,5 @@ true, 'selfServiceModeShowOnlyStockProducts' => true, 'selfServiceModeAutoGenerateInvoice' => true, + 'showOrderedProductsTotalAmountInCatalog' => false, /** * id of the category "all products" @@ -147,18 +151,6 @@ 'countryId' => 2, // austria: 2, germany: 1 - /** - * if you work on windows, change to e.g - * '"C:\\Programme\\xampp\\mysql\\bin\\mysqldump.exe"' - */ - 'mysqlDumpCommand' => 'mysqldump', - - /** - * if you work on windows, change to e.g - * '"C:\\Programme\\xampp\\mysql\\bin\\mysql.exe"' - */ - 'mysqlCommand' => 'mysql', - /** * date of the last update of terms of use */ @@ -189,8 +181,9 @@ 'showStatisticsForAdmins' => true, 'sendEmailWhenOrderDetailQuantityChanged' => true, - - 'sendEmailWhenOrderDetailCustomerChanged' => true, + + // if set, a paypal.me-link is added to the invoice-to-customer email + 'paypalMeUsername' => '', 'helloCashRestEndpoint' => 'https://api.hellocash.business/api/v1', diff --git a/config/app_queue.php b/config/app_queue.php index d6892a72f4..6c76306dbc 100644 --- a/config/app_queue.php +++ b/config/app_queue.php @@ -1,4 +1,6 @@ 'aa']); -// foodcoopshop -mb_internal_encoding('UTF-8'); - -FactoryLocator::get('Table')->get('Configurations')->loadConfigurations(); -if (in_array(Configure::read('appDb.FCS_DEFAULT_LOCALE'), Configure::read('app.implementedLocales'))) { - ini_set('intl.default_locale', Configure::read('appDb.FCS_DEFAULT_LOCALE')); - locale_set_default(Configure::read('appDb.FCS_DEFAULT_LOCALE')); - I18n::setLocale(Configure::read('appDb.FCS_DEFAULT_LOCALE')); - Configure::load('Locale' . DS . Configure::read('appDb.FCS_DEFAULT_LOCALE') . DS . 'date', 'default'); - setlocale(LC_CTYPE, Configure::read('appDb.FCS_DEFAULT_LOCALE').'.UTF-8'); - setlocale(LC_COLLATE, Configure::read('appDb.FCS_DEFAULT_LOCALE').'.UTF-8'); -} - // Number::config(Configure::read('appDb.FCS_DEFAULT_LOCALE'), NumberFormatter::DECIMAL); // TypeFactory::build('decimal')->useLocaleParser(); -// gettext not available in app_config -Configure::load('localized_config', 'default'); - -if (file_exists(CONFIG.DS.'localized_custom_config.php')) { - Configure::load('localized_custom_config', 'default'); -} - diff --git a/config/bootstrap_cli.php b/config/bootstrap_cli.php index d3679d7547..cd66872d92 100644 --- a/config/bootstrap_cli.php +++ b/config/bootstrap_cli.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +use Cake\Core\Configure; +use Cake\Datasource\FactoryLocator; +use Cake\I18n\I18n; + +mb_internal_encoding('UTF-8'); +try { + // on fresh installations there is no configurations table yet when first migrations run + FactoryLocator::get('Table')->get('Configurations')->loadConfigurations(); +} catch(\Exception $e) {} +if (in_array(Configure::read('appDb.FCS_DEFAULT_LOCALE'), Configure::read('app.implementedLocales'))) { + ini_set('intl.default_locale', Configure::read('appDb.FCS_DEFAULT_LOCALE')); + locale_set_default(Configure::read('appDb.FCS_DEFAULT_LOCALE')); + I18n::setLocale(Configure::read('appDb.FCS_DEFAULT_LOCALE')); + Configure::load('Locale' . DS . Configure::read('appDb.FCS_DEFAULT_LOCALE') . DS . 'date', 'default'); + setlocale(LC_CTYPE, Configure::read('appDb.FCS_DEFAULT_LOCALE').'.UTF-8'); + setlocale(LC_COLLATE, Configure::read('appDb.FCS_DEFAULT_LOCALE').'.UTF-8'); +} + +// gettext not available in app_config +Configure::load('localized_config', 'default'); + +if (file_exists(CONFIG.DS.'localized_custom_config.php')) { + Configure::load('localized_custom_config', 'default'); +} + +?> \ No newline at end of file diff --git a/config/credentials.default.php b/config/credentials.default.php index ac1ca9cd4d..bdaa06911d 100644 --- a/config/credentials.default.php +++ b/config/credentials.default.php @@ -1,4 +1,6 @@ [ + //* BE AWARE: NO TRAILING SLASH! + 'fullBaseUrl' => false, + ], + 'app' => [ 'discourseSsoEnabled' => false, @@ -76,11 +83,6 @@ */ 'discourseSsoSecret' => '', - /** - * your host's name, eg. http://www.yourfoodcoop.com - */ - 'cakeServerName' => '', - /** * cronjob needs to be activated / deactivated too if you change emailOrderReminderEnabled * @see https://foodcoopshop.github.io/en/cronjobs diff --git a/.github/workflows/custom_config.php b/config/custom_config.dev.php similarity index 50% rename from .github/workflows/custom_config.php rename to config/custom_config.dev.php index cb3ea0679a..14c466e713 100644 --- a/.github/workflows/custom_config.php +++ b/config/custom_config.dev.php @@ -1,10 +1,10 @@ true, 'EmailTransport' => [ 'default' => [ - 'className' => 'Mail', + 'className' => 'Smtp', + 'host' => '', + 'port' => 25, + 'timeout' => 30, + 'username' => '', + 'password' => '', + 'client' => null, + 'tls' => null, ] ], 'Email' => [ 'default' => [ - 'transport' => 'default', - 'from' => 'you@localhost' - ], + 'from' => [], // [email-address => name] syntax necessary (not only [email] + ] ], 'Datasources' => [ 'default' => [ - 'host' => '127.0.0.1', - 'username' => 'root', - 'password' => 'password', - 'database' => 'foodcoopshop_test', - 'port' => '8888', + 'host' => 'database-dev', + 'username' => 'user', + 'password' => 'secret', + 'database' => 'foodcoopshop-dev', + 'port' => 3320, ], 'test' => [ - 'host' => '127.0.0.1', - 'username' => 'root', - 'password' => 'password', - 'database' => 'foodcoopshop_test', - 'port' => '8888', - ] + 'host' => 'database-test', + 'username' => 'user', + 'password' => 'secret', + 'database' => 'foodcoopshop-test', + 'port' => 3321, + ], ], /** * A random string used in security hashing methods. */ - 'Security.salt' => 'b6OSgpEV0vA36P3PxjWigmbQc6J5CLhs3bSV89KK8m1IKkl8gJfp84Odz3gMdW9K', + 'Security' => [ + 'salt' => '3f4c77c698213b1ee8d0eca929340b69ee555c7585c99a17347991c8c9260f44' + ], + + 'Cache' => [ + 'default' => [ + 'prefix' => 'example_com_', + ], + 'short' => [ + 'prefix' => 'example_com_', + ], + '_cake_core_' => [ + 'prefix' => 'example_com_', + ], + '_cake_model_' => [ + 'prefix' => 'example_com_', + ], + ], + + 'App' => [ + //* BE AWARE: NO TRAILING SLASH! + 'fullBaseUrl' => 'http://localhost', + ], 'app' => [ + + 'discourseSsoEnabled' => false, + /** - * your host's name, eg. http://www.yourfoodcoop.com + * A random string used for Discourse SSO */ - 'cakeServerName' => 'http://localhost', + 'discourseSsoSecret' => '', /** - * cronjob needs to be activated too + * cronjob needs to be activated / deactivated too if you change emailOrderReminderEnabled + * @see https://foodcoopshop.github.io/en/cronjobs */ 'emailOrderReminderEnabled' => true, - 'outputStringReplacements' => [ - 'This is a test' => 'This is another test', - ], - /** * valid options of array: 'cashless' or 'cash' (or both but this is not recommended) */ diff --git a/config/elfinder/php/connector.minimal.php b/config/elfinder/php/connector.minimal.php index dda09b0660..b0845d4274 100644 --- a/config/elfinder/php/connector.minimal.php +++ b/config/elfinder/php/connector.minimal.php @@ -1,4 +1,6 @@
Ab welcher verfügbaren Produkt-Anzahl soll beim Bestellen der Hinweis \"(x verfügbar\") angezeigt werden?
','10','number',600,'de_DE','2017-07-26 13:24:47','2014-06-01 01:40:34'), -(31,1,'FCS_DAYS_SHOW_PRODUCT_AS_NEW','Wie viele Tage sollen Produkte \"als neu markiert\" bleiben?','7','number',700,'de_DE','2017-07-26 13:24:47','2014-05-14 21:15:45'), -(456,1,'FCS_FOOTER_CMS_TEXT','Zusätzlicher Text für den Footer',NULL,'textarea_big',920,'de_DE','2014-06-11 17:50:55','2016-07-01 21:47:47'), -(508,1,'FCS_FACEBOOK_URL','Facebook-Url für die Einbindung im Footer','https://www.facebook.com/FoodCoopShop/','text',910,'de_DE','2015-07-08 13:23:54','2015-07-08 13:23:54'), -(538,1,'FCS_REGISTRATION_EMAIL_TEXT','Zusätzlicher Text, der in der Bestätigungsmail nach einer Registrierung versendet wird.
E-Mail-Vorschau anzeigen','','textarea_big',1700,'de_DE','2016-06-26 00:00:00','2016-06-26 00:00:00'), -(543,1,'FCS_RIGHT_INFO_BOX_HTML','Inhalt der Box in der rechten Spalte unterhalb des Warenkorbes.
Um eine Zeile grün zu hinterlegen (Überschrift) bitte als \"Überschrift 3\" formatieren.
','

Abholzeiten

\r\n\r\n

Du kannst jede Woche bis spätestens Dienstag Mitternacht bestellen und die Produkte am Freitag abholen.

\r\n','textarea_big',1500,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(544,1,'FCS_NO_DELIVERY_DAYS_GLOBAL','Lieferpause für alle Hersteller?
Hier können lieferfreie Tage (z.B. Feiertage) für die gesamte Foodcoop festgelegt werden.
','','multiple_dropdown',100,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(545,1,'FCS_ACCOUNTING_EMAIL','E-Mail-Adresse des Finanzverantwortlichen
Wer bekommt die Benachrichtigung über den erfolgten Rechnungsversand?
','','text',1100,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(546,1,'FCS_REGISTRATION_INFO_TEXT','Info-Text beim Registrierungsformular
Beim Registrierungsformlar wird unterhalb der E-Mail-Adresse dieser Text angezeigt.
','Um bei uns zu bestellen musst du Vereinsmitglied sein.','textarea_big',1600,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(547,1,'FCS_SHOW_PRODUCTS_FOR_GUESTS','Produkte für nicht eingeloggte Mitglieder sichtbar?','0','boolean',200,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(548,1,'FCS_DEFAULT_NEW_MEMBER_ACTIVE','Neue Mitglieder automatisch aktivieren?','0','boolean',500,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(549,1,'FCS_MINIMAL_CREDIT_BALANCE','Bis zu welchem Guthaben-Betrag sollen Bestellungen möglich sein?','0','number',1250,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(550,1,'FCS_BANK_ACCOUNT_DATA','Bankverbindung für die Guthaben-Einzahlungen\".','Guthaben-Konto Testbank / IBAN: AT65 5645 4154 8748 8999 / BIC: ABC87878','text',1300,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(552,1,'FCS_DELIVERY_DETAILS_FOR_MANUFACTURERS','Zusätzliche Liefer-Informationen für die Hersteller
wird in den Bestell-Listen nach dem Lieferdatum angezeigt.
',', 15:00 bis 17:00 Uhr','text',1200,'de_DE','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(553,1,'FCS_BACKUP_EMAIL_ADDRESS_BCC','E-Mail-Adresse, an die sämtliche vom System generierten E-Mails als BCC verschickt werden (Backup).
Kann leer gelassen werden.
','','text',1900,'de_DE','2016-10-06 00:00:00','2016-10-06 00:00:00'), -(554,1,'FCS_SHOW_FOODCOOPSHOP_BACKLINK','Auf Homepage Link auf www.foodcoopshop.com anzeigen?
Der Link wird im Footer angezeigt.
','1','boolean',930,'de_DE','2016-11-27 00:00:00','2016-11-27 00:00:00'), -(556,1,'FCS_APP_NAME','Name der Foodcoop','','text',50,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(557,1,'FCS_APP_ADDRESS','Adresse der Foodcoop
Wird im Footer von Homepage und E-Mails, Datenschutzerklärung, Nutzungsbedingungen usw. verwendet.
','','textarea',60,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(558,1,'FCS_APP_EMAIL','E-Mail-Adresse der Foodcoop
','','text',900,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(559,1,'FCS_PLATFORM_OWNER','Betreiber der Plattform
Für Datenschutzerklärung und Nutzungsbedingungen, bitte auch Adresse angeben. Kann leer gelassen werden, wenn die Foodcoop selbst die Plattform betreibt.
','','textarea',90,'de_DE','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(564,1,'FCS_ORDER_COMMENT_ENABLED','Kommentarfeld bei Bestell-Abschluss anzeigen?
Wird im Admin-Bereich unter \"Bestellungen\" angezeigt.
','1','boolean',130,'de_DE','2017-07-09 00:00:00','2017-07-09 00:00:00'), -(565,1,'FCS_USE_VARIABLE_MEMBER_FEE','Variablen Mitgliedsbeitrag verwenden?
Den variablen Mitgliedsbeitrag bei den Hersteller-Rechnungen abziehen? Die Produkt-Preise müssen entsprechend höher eingegeben werden.
','0','readonly',400,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), -(566,1,'FCS_DEFAULT_VARIABLE_MEMBER_FEE_PERCENTAGE','Standardwert für variablen Mitgliedsbeitrag
Der Prozentsatz kann in den Hersteller-Einstellungen auch individuell angepasst werden.
','0','readonly',500,'de_DE','2017-08-02 00:00:00','2017-08-02 00:00:00'), -(567,1,'FCS_NETWORK_PLUGIN_ENABLED','Netzwerk-Modul aktiviert?
Infos zum Netzwerk-Modul
','0','readonly',500,'de_DE','2017-09-14 00:00:00','2017-09-14 00:00:00'), -(574,1,'FCS_SHOW_PRODUCT_PRICE_FOR_GUESTS','Produktpreis für nicht eingeloggte Mitglieder anzeigen?','0','boolean',210,'de_DE','2018-05-28 18:05:54','2018-05-28 18:05:54'), -(575,1,'FCS_CURRENCY_SYMBOL','Währungssymbol','€','readonly',520,'de_DE','2018-06-13 19:53:14','2018-06-13 19:53:14'), -(576,1,'FCS_DEFAULT_LOCALE','Sprache','de_DE','readonly',550,'de_DE','2018-06-26 10:18:55','2018-06-26 10:18:55'), -(577,1,'FCS_FOODCOOPS_MAP_ENABLED','Auf Home Karte mit anderen Foodcoops anzeigen?','1','boolean',1280,'de_DE','2019-02-11 22:25:36','2019-02-11 22:25:36'), -(578,1,'FCS_WEEKLY_PICKUP_DAY','Wöchentlicher Abholtag','5','readonly',600,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(579,1,'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA','Bestelllisten-Versand: x Tage vor dem Abholtag','2','readonly',650,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(580,1,'FCS_ORDER_POSSIBLE_FOR_STOCK_PRODUCTS_IN_ORDERS_WITH_DELIVERY_RHYTHM','Sollen Lagerprodukte mit der wöchentlichen Bestellung bestellt werden können?','1','boolean',750,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(581,1,'FCS_SHOW_NON_STOCK_PRODUCTS_IN_INSTANT_ORDERS','In der Sofort-Bestellung ausschließlich Lagerprodukte anzeigen?','0','boolean',760,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(582,1,'FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES','Lagerprodukte in Rechnungen miteinbeziehen?','1','readonly',600,'de_DE','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(583,1,'FCS_REGISTRATION_NOTIFICATION_EMAILS','Wer soll bei neuen Registrierungen informiert werden?
Mehrere E-Mail-Adressen mit , (ohne Leerzeichen) trennen.
','','text',550,'de_DE','2019-03-05 20:01:59','2019-03-05 20:01:59'), -(584,1,'FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED','Selbstbedienungs-Modus für Lagerprodukte aktiv?
Zur Online-Doku
','0','boolean',3000,'de_DE','2019-04-17 20:01:59','2019-04-17 20:01:59'), -(585,1,'FCS_APP_ADDITIONAL_DATA','Zusätzliche Infos zur Foodcoop
Z.B. ZVR-Zahl
','','textarea',80,'de_DE','2019-08-03 20:07:04','2019-08-03 20:07:04'), -(586,1,'FCS_SELF_SERVICE_MODE_TEST_MODE_ENABLED','Selbstbedienungs-Modus im Test-Modus ausführen?
Keine Verlinkung im Haupt-Menü und bei Lagerprodukten.
','0','boolean',3100,'de_DE','2019-12-09 13:46:27','2019-12-09 13:46:27'), -(587,1,'FCS_CASHLESS_PAYMENT_ADD_TYPE','Art der Eintragung der Guthaben-Aufladungen
Wie gelangen die Guthaben-Aufladungen vom Bankkonto in den FoodCoopShop?
','list-upload','dropdown',1450,'de_DE','2020-02-11 10:12:57','2020-02-11 10:12:57'), -(589,1,'FCS_FEEDBACK_TO_PRODUCTS_ENABLED','Feedback-Funktion für Produkte aktiviert?
Mitglieder können Feedback zu bestellten Produkte verfassen.
','0','boolean',3200,'de_DE','2020-06-19 09:02:46','2020-06-19 09:02:46'), -(590,1,'FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY','Mitglied kann Abholtag beim Bestellen selbst auswählen.','0','readonly',590,'de_DE','2020-07-06 10:34:35','2020-07-06 10:34:35'), -(591,1,'FCS_SEND_INVOICES_TO_CUSTOMERS','Einzelhandels-Modus aktiviert?
Infos zur Verwendung im Einzelhandel
','0','readonly',580,'de_DE','2020-10-29 10:06:34','2020-10-29 10:06:34'), -(592,1,'FCS_DEPOSIT_TAX_RATE','Umsatzsteuersatz für Pfand','20,00','readonly',581,'de_DE','2020-11-03 15:23:55','2020-11-03 15:23:55'), -(593,1,'FCS_INVOICE_HEADER_TEXT','Header-Text für Rechnungen an Mitglieder','','readonly',582,'de_DE','2020-11-03 15:23:55','2020-11-03 15:23:55'), -(594,1,'FCS_MEMBER_FEE_PRODUCTS','Welche Produkte werden als Mitgliedsbeitrag verwendet?
Die ausgewählten Produkte sind Datengrundlage der Spalte Mitgliedsbeitrag in der Mitgliederverwaltung und werden nicht in der Umsatzstatistik angezeigt.
','','multiple_dropdown',3300,'de_DE','2020-12-20 19:26:10','2020-12-20 19:26:10'), -(595,1,'FCS_CHECK_CREDIT_BALANCE_LIMIT','Ab welchem Guthaben-Stand soll die Erinnerungsmail versendet werden?','50','number',1450,'de_DE','2021-01-19 11:23:34','2021-01-19 11:23:34'), -(596,1,'FCS_PURCHASE_PRICE_ENABLED','Einkaufspreis für Produkte erfassen?
Der Einkaufspreis ist die Datengrundlage für die Gewinn-Statistik und für Lieferscheine an die Hersteller.
','0','readonly',587,'de_DE','2021-05-10 11:27:38','2021-05-10 11:27:38'), -(597,1,'FCS_HELLO_CASH_API_ENABLED','Schnittstelle (API) zu Registrierkasse HelloCash (hellocash.at) aktivieren?
Alle Rechnungen (bar und unbar) über die Registrierkasse erstellen.
','0','readonly',583,'de_DE','2021-07-07 10:55:03','2021-07-07 10:55:03'), -(598,1,'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS','Lagerort für Produkte erfassen und in Bestelllisten anzeigen?
Lagerorte: Keine Kühlung / Kühlschrank / Tiefkühler. Es erscheint ein zusätzlicher Button neben \"Bestellungen - Bestellungen als PDF generieren\"
','1','boolean',3210,'de_DE','2021-08-02 11:28:29','2021-08-02 11:28:29'), -(599,1,'FCS_INSTAGRAM_URL','Instagram-Url für die Einbindung im Footer','','text',920,'de_DE','2021-09-10 21:23:08','2021-09-10 21:23:08'), -(600,1,'FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY','Bestellungen beim ein- und zweiwöchigen Lieferhythmus sind nur in der Woche vor der Lieferung möglich.','0','boolean',3210,'de_DE','2022-02-01 17:48:35','2022-02-01 17:48:35'), -(601,1,'FCS_INVOICE_NUMBER_PREFIX','Präfix für Rechnungs-Nummernkreis
Max. 6 Zeichen inkl. Trennzeichen.
','','readonly',586,'de_DE','2022-03-21 12:02:48','2022-03-21 12:02:48'), -(602,1,'FCS_TAX_BASED_ON_NET_INVOICE_SUM','Rechnungslegung für pauschalierte Betriebe
Die Berechnung der Umsatzsteuer erfolgt auf Basis der Netto-Rechnungsumme und ist nicht die Summe der Umsatzsteuerbeträge pro Stück.
','0','readonly',585,'de_DE','2022-03-23 09:12:23','2022-03-23 09:12:23'), -(603,1,'FCS_NEWSLETTER_ENABLED','Newsletter-Funktion aktiv?
Mitglieder können sich bei der Registrierung für den Newsletter anmelden. Mehr Infos
','0','boolean',3400,'de_DE','2022-04-12 15:28:47','2022-04-12 15:28:47'), -(604,1,'FCS_USER_FEEDBACK_ENABLED','Mitglieder- und Hersteller-Feedback aktiv?
Ermöglicht das Erfassen und Anzeigen von Feedback. Mehr Infos
','0','boolean',3500,'de_DE','2022-07-19 14:39:27','2022-07-19 14:39:27'); -/*!40000 ALTER TABLE `fcs_configuration` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_cronjob_logs` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_cronjob_logs` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_cronjobs` DISABLE KEYS */; -INSERT INTO `fcs_cronjobs` VALUES -(1,'BackupDatabase','day',NULL,NULL,'04:00:00',1), -(2,'CheckCreditBalance','week',NULL,'Friday','22:30:00',1), -(3,'EmailOrderReminder','week',NULL,'Monday','18:00:00',1), -(4,'PickupReminder','week',NULL,'Monday','09:00:00',1), -(5,'SendInvoicesToManufacturers','month',11,NULL,'10:30:00',1), -(6,'SendOrderLists','day',NULL,NULL,'04:30:00',1), -(7,'SendInvoicesToCustomers','week',NULL,'Saturday','10:00:00',0), -(8,'SendDeliveryNotes','month',1,NULL,'18:00:00',0); -/*!40000 ALTER TABLE `fcs_cronjobs` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_customer` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_customer` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_deposits` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_deposits` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_feedbacks` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_feedbacks` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_images` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_images` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_invoice_taxes` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_invoice_taxes` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_invoices` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_invoices` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_manufacturer` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_manufacturer` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail_feedbacks` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail_feedbacks` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail_purchase_prices` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail_purchase_prices` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail_units` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail_units` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_pages` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_pages` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_payments` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_payments` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_pickup_days` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_pickup_days` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_product` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_product` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_product_attribute` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_product_attribute` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_product_attribute_combination` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_product_attribute_combination` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_purchase_prices` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_purchase_prices` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_sliders` DISABLE KEYS */; -INSERT INTO `fcs_sliders` VALUES -(1,'demo-slider.jpg',NULL,0,0,1); -/*!40000 ALTER TABLE `fcs_sliders` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_stock_available` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_stock_available` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_storage_locations` DISABLE KEYS */; -INSERT INTO `fcs_storage_locations` VALUES -(1,'Keine Kühlung',10), -(2,'Kühlschrank',20), -(3,'Tiefkühler',30); -/*!40000 ALTER TABLE `fcs_storage_locations` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_sync_domains` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_sync_domains` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_sync_products` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_sync_products` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_tax` DISABLE KEYS */; -INSERT INTO `fcs_tax` VALUES -(1,20.000,1,0), -(2,10.000,1,0), -(3,13.000,1,0); -/*!40000 ALTER TABLE `fcs_tax` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_units` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_units` ENABLE KEYS */; - -/*!40000 ALTER TABLE `phinxlog` DISABLE KEYS */; -INSERT INTO `phinxlog` VALUES -(20200404145856,'RemoveV2Migrations','2020-04-04 15:01:04','2020-04-04 15:01:04',0), -(20200415073329,'ShowNewProductsOnHome','2020-04-15 07:41:54','2020-04-15 07:41:54',0), -(20200501192722,'EnableCashlessPaymentAddTypeConfiguration','2020-05-01 19:30:09','2020-05-01 19:30:09',0), -(20200618063024,'AddProductFeedback','2020-06-19 07:02:46','2020-06-19 07:02:46',0), -(20200703072605,'CustomerCanSelectPickupDay','2020-07-06 08:34:35','2020-07-06 08:34:35',0), -(20200831142250,'RemoveEmailLogTable','2020-08-31 15:10:21','2020-08-31 15:10:21',0), -(20200910091755,'AddMemberSettingUseCameraForMobileBarcodeScanning','2020-09-10 09:20:50','2020-09-10 09:20:51',0), -(20200925073919,'GermanIbanFix','2020-09-25 08:12:37','2020-09-25 08:12:37',0), -(20201017182431,'AdaptMinimalCreditBalance','2020-10-17 18:37:54','2020-10-17 18:37:54',0), -(20201029084931,'AddRetailMode','2020-10-29 09:06:34','2020-10-29 09:06:34',0), -(20201118084516,'AddRetailMode2','2020-11-18 08:47:32','2020-11-18 08:47:32',0), -(20201213120713,'AddRetailMode3','2020-12-13 12:13:56','2020-12-13 12:13:56',0), -(20201217101514,'SliderWithLink','2020-12-17 10:26:31','2020-12-17 10:26:31',0), -(20201220182015,'ImproveMemberFeeAdministration','2020-12-20 18:26:10','2020-12-20 18:26:10',0), -(20210119101923,'CheckCreditBalanceLimit','2021-01-19 10:23:34','2021-01-19 10:23:34',0), -(20210401071718,'RemoveCustomerGroupSetting','2021-04-01 07:18:43','2021-04-01 07:18:43',0), -(20210401082727,'CustomerActivateEmailCode','2021-04-01 08:29:12','2021-04-01 08:29:12',0), -(20210419084816,'BlogPostShowOnStartPageUntilDate','2021-04-19 09:41:13','2021-04-19 09:41:13',0), -(20210427144234,'RemoveOldMemberFeeSetting','2021-04-27 15:04:53','2021-04-27 15:04:53',0), -(20210504085123,'SaveTaxInOrderDetails','2021-05-04 09:10:04','2021-05-04 09:10:04',0), -(20210510080630,'EnablePurchasePrices','2021-05-10 09:27:38','2021-05-10 09:27:38',0), -(20210707083827,'AddRegistrierkasseApi','2021-07-07 08:55:03','2021-07-07 08:55:03',0), -(20210802090623,'AddStorageLocation','2021-08-02 09:28:29','2021-08-02 09:28:29',0), -(20210910191430,'Instagram','2021-09-10 19:23:08','2021-09-10 19:23:08',0), -(20210914071747,'DifferentPricesForCustomers','2021-09-16 05:50:01','2021-09-16 05:50:01',0), -(20210922154148,'RemoveUnusedQueueTable','2021-09-22 15:42:59','2021-09-22 15:42:59',0), -(20210923073422,'RemoveSettingShowNewProductsOnHome','2021-09-23 07:39:22','2021-09-23 07:39:22',0), -(20210923090820,'AllowNullAsPurchasePrice','2021-09-23 09:09:41','2021-09-23 09:09:41',0), -(20211028083847,'UseExistingBarcode','2021-10-28 08:44:51','2021-10-28 08:44:51',0), -(20211123095227,'DeactivateCheckCreditReminder','2021-11-23 10:00:54','2021-11-23 10:00:54',0), -(20211213081433,'ImproveCustomerNotifications','2021-12-13 09:24:34','2021-12-13 09:24:34',0), -(20211213081434,'ImproveCustomerNotifications','2021-12-13 10:44:45','2021-12-13 10:44:45',0), -(20211215184633,'ManufacturerSettingIncludeStockProductsOnOrderList','2021-12-15 18:50:50','2021-12-15 18:50:50',0), -(20211229194617,'AddIndizesForBetterPerformance','2021-12-29 19:55:28','2021-12-29 19:55:28',0), -(20220129082136,'SendDeliveryNotesEveryMonth','2022-01-29 08:31:51','2022-01-29 08:31:51',0), -(20220201163254,'OptionalDeliveryRhythmSettingOrderInWeekBeforeDelivery','2022-02-01 16:48:35','2022-02-01 16:48:35',0), -(20220321103059,'PrefixForInvoices','2022-03-21 11:02:48','2022-03-21 11:02:48',0), -(20220323075926,'TaxBasedOnNetInvoiceSum','2022-03-23 08:12:23','2022-03-23 08:12:23',0), -(20220407093247,'AddIsCompanyFieldForCustomer','2022-04-07 09:37:26','2022-04-07 09:37:26',0), -(20220412131842,'Newsletter','2022-04-12 13:28:47','2022-04-12 13:28:47',0), -(20220525092822,'BiggerQueuedJobDataField','2022-05-25 09:31:51','2022-05-25 09:31:51',0), -(20220620091755,'RemoveTimebasedCurrencyModule','2022-06-20 09:30:19','2022-06-20 09:30:19',0), -(20220717194215,'UserFeedback','2022-07-19 12:39:27','2022-07-19 12:39:27',0); -/*!40000 ALTER TABLE `phinxlog` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queue_phinxlog` DISABLE KEYS */; -INSERT INTO `queue_phinxlog` VALUES -(20150425180802,'Init','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20150511062806,'Fixmissing','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20150911132343,'ImprovementsForMysql','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20161319000000,'IncreaseDataSize','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20161319000001,'Priority','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20161319000002,'Rename','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20161319000003,'Processes','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20171013131845,'AlterQueuedJobs','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20171013133145,'Utf8mb4Fix','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20171019083500,'ColumnLength','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20171019083501,'MigrationQueueNull','2020-09-17 07:23:19','2020-09-17 07:23:19',0), -(20171019083502,'MigrationQueueStatus','2020-09-17 07:23:19','2020-09-17 07:23:20',0), -(20171019083503,'MigrationQueueProcesses','2020-09-17 07:23:20','2020-09-17 07:23:20',0), -(20171019083505,'MigrationQueueProcessesIndex','2020-09-17 07:23:20','2020-09-17 07:23:20',0), -(20171019083506,'MigrationQueueProcessesKey','2020-09-17 07:23:20','2020-09-17 07:23:20',0), -(20191319000002,'MigrationQueueRename','2021-07-20 11:12:52','2021-07-20 11:12:52',0); -/*!40000 ALTER TABLE `queue_phinxlog` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queue_processes` DISABLE KEYS */; -/*!40000 ALTER TABLE `queue_processes` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queued_jobs` DISABLE KEYS */; -/*!40000 ALTER TABLE `queued_jobs` ENABLE KEYS */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - diff --git a/config/sql/_installation/clean-db-data-en_US.sql b/config/sql/_installation/clean-db-data-en_US.sql deleted file mode 100644 index 18ae036859..0000000000 --- a/config/sql/_installation/clean-db-data-en_US.sql +++ /dev/null @@ -1,328 +0,0 @@ - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- Truncate tables before insertion -TRUNCATE TABLE `fcs_action_logs`; -TRUNCATE TABLE `fcs_address`; -TRUNCATE TABLE `fcs_attribute`; -TRUNCATE TABLE `fcs_barcodes`; -TRUNCATE TABLE `fcs_blog_posts`; -TRUNCATE TABLE `fcs_cart_product_units`; -TRUNCATE TABLE `fcs_cart_products`; -TRUNCATE TABLE `fcs_carts`; -TRUNCATE TABLE `fcs_category`; -TRUNCATE TABLE `fcs_category_product`; -TRUNCATE TABLE `fcs_configuration`; -TRUNCATE TABLE `fcs_cronjob_logs`; -TRUNCATE TABLE `fcs_cronjobs`; -TRUNCATE TABLE `fcs_customer`; -TRUNCATE TABLE `fcs_deposits`; -TRUNCATE TABLE `fcs_feedbacks`; -TRUNCATE TABLE `fcs_images`; -TRUNCATE TABLE `fcs_invoice_taxes`; -TRUNCATE TABLE `fcs_invoices`; -TRUNCATE TABLE `fcs_manufacturer`; -TRUNCATE TABLE `fcs_order_detail`; -TRUNCATE TABLE `fcs_order_detail_feedbacks`; -TRUNCATE TABLE `fcs_order_detail_purchase_prices`; -TRUNCATE TABLE `fcs_order_detail_units`; -TRUNCATE TABLE `fcs_pages`; -TRUNCATE TABLE `fcs_payments`; -TRUNCATE TABLE `fcs_pickup_days`; -TRUNCATE TABLE `fcs_product`; -TRUNCATE TABLE `fcs_product_attribute`; -TRUNCATE TABLE `fcs_product_attribute_combination`; -TRUNCATE TABLE `fcs_purchase_prices`; -TRUNCATE TABLE `fcs_sliders`; -TRUNCATE TABLE `fcs_stock_available`; -TRUNCATE TABLE `fcs_storage_locations`; -TRUNCATE TABLE `fcs_sync_domains`; -TRUNCATE TABLE `fcs_sync_products`; -TRUNCATE TABLE `fcs_tax`; -TRUNCATE TABLE `fcs_units`; -TRUNCATE TABLE `phinxlog`; -TRUNCATE TABLE `queue_phinxlog`; -TRUNCATE TABLE `queue_processes`; -TRUNCATE TABLE `queued_jobs`; - -/*!40000 ALTER TABLE `fcs_action_logs` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_action_logs` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_address` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_address` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_attribute` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_attribute` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_barcodes` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_barcodes` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_blog_posts` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_blog_posts` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_cart_product_units` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_cart_product_units` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_cart_products` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_cart_products` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_carts` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_carts` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_category` DISABLE KEYS */; -INSERT INTO `fcs_category` VALUES -(20,2,'All products','',3,4,1,'2016-10-19 21:05:00','2016-10-19 21:05:00'); -/*!40000 ALTER TABLE `fcs_category` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_category_product` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_category_product` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_configuration` DISABLE KEYS */; -INSERT INTO `fcs_configuration` VALUES -(11,1,'FCS_PRODUCT_AVAILABILITY_LOW','Low availability
From which amount on there should be an information text visible \"(x available\")?
','10','number',600,'en_US','2017-07-26 13:24:47','2014-06-01 01:40:34'), -(31,1,'FCS_DAYS_SHOW_PRODUCT_AS_NEW','How many days products should be \"marked as new\"?','7','number',700,'en_US','2017-07-26 13:24:47','2014-05-14 21:15:45'), -(456,1,'FCS_FOOTER_CMS_TEXT','Additional text for footer',NULL,'textarea_big',920,'en_US','2014-06-11 17:50:55','2016-07-01 21:47:47'), -(508,1,'FCS_FACEBOOK_URL','Facebook url for embedding in footer','https://www.facebook.com/FoodCoopShop/','text',910,'en_US','2015-07-08 13:23:54','2015-07-08 13:23:54'), -(538,1,'FCS_REGISTRATION_EMAIL_TEXT','Additional text that is sent in the registration e-mail after a successful registration.
E-mail preview','','textarea_big',1700,'en_US','2016-06-26 00:00:00','2016-06-26 00:00:00'), -(543,1,'FCS_RIGHT_INFO_BOX_HTML','Content of the box in the right column below the shopping cart.
To make the background of a row green, please format as \"Heading 3\".
','

Delivery time

You can order every week until Tuesday midnight and pick the products up the following Friday.

','textarea_big',1500,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(544,1,'FCS_NO_DELIVERY_DAYS_GLOBAL','Delivery break for all manufacturers?
Here you can define delivery-free days for the whole food-coop.
','','multiple_dropdown',100,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(545,1,'FCS_ACCOUNTING_EMAIL','E-mail address for the financial manager
Who receives the notification that invoices have been sent?
','','text',1100,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(546,1,'FCS_REGISTRATION_INFO_TEXT','Info text in registration form
This info text is shown in the registration form below the e-mail address.
','You need to be a member if you want to order here.','textarea_big',1600,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(547,1,'FCS_SHOW_PRODUCTS_FOR_GUESTS','Products visible for guests?','0','boolean',200,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(548,1,'FCS_DEFAULT_NEW_MEMBER_ACTIVE','Automatically activate new members?','0','boolean',500,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(549,1,'FCS_MINIMAL_CREDIT_BALANCE','Up to which credit amount orders should be possible?','0','number',1250,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(550,1,'FCS_BANK_ACCOUNT_DATA','Bank account for credit uploads.','Credit account Example Bank / IBAN: AT65 5645 4154 8748 8999 / BIC: ABC87878','text',1300,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(552,1,'FCS_DELIVERY_DETAILS_FOR_MANUFACTURERS','Additional deliverey details for manufacturers
will be shown in the order lists after the delivery date.
',', 3pm to 5pm','text',1200,'en_US','2017-07-26 13:24:47','2017-07-26 13:24:47'), -(553,1,'FCS_BACKUP_EMAIL_ADDRESS_BCC','E-mail adress to which all automatically generated e-mail are sent to as BCC (Backup).
Can be left empty.
','','text',1900,'en_US','2016-10-06 00:00:00','2016-10-06 00:00:00'), -(554,1,'FCS_SHOW_FOODCOOPSHOP_BACKLINK','Show link to www.foodcoopshop.com?
The link is shown in the footer.
','1','boolean',930,'en_US','2016-11-27 00:00:00','2016-11-27 00:00:00'), -(556,1,'FCS_APP_NAME','Name of the food-coop','','text',50,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(557,1,'FCS_APP_ADDRESS','Adress of the food-coop
Used in footer of homepage and e-mails, privacy policy and terms of use.
','','textarea',60,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(558,1,'FCS_APP_EMAIL','E-mail adress of the food-coop
','','text',900,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(559,1,'FCS_PLATFORM_OWNER','Operator of the platform
For privacy policy and terms of use, please also add adrress. Can be left empty if the food-coop itself is operator.
','','textarea',90,'en_US','2017-01-12 00:00:00','2017-01-12 00:00:00'), -(564,1,'FCS_ORDER_COMMENT_ENABLED','Show comment field when placing an order?
Shown in admin area under \"Orders\".
','1','boolean',130,'en_US','2017-07-09 00:00:00','2017-07-09 00:00:00'), -(565,1,'FCS_USE_VARIABLE_MEMBER_FEE','Use variable member fee?
Reduce the variable member fee in the manufacturer\'s invoices? Therefore the prices need to be increased.
','0','readonly',400,'en_US','2017-08-02 00:00:00','2017-08-02 00:00:00'), -(566,1,'FCS_DEFAULT_VARIABLE_MEMBER_FEE_PERCENTAGE','Default value for variable member fee
The percentage can be changed in the manufacturer\'s settings.
','0','readonly',500,'en_US','2017-08-02 00:00:00','2017-08-02 00:00:00'), -(567,1,'FCS_NETWORK_PLUGIN_ENABLED','Network module activated?
Infos to the network module
','0','readonly',500,'en_US','2017-09-14 00:00:00','2017-09-14 00:00:00'), -(574,1,'FCS_SHOW_PRODUCT_PRICE_FOR_GUESTS','Shop product price for guests?','0','boolean',210,'en_US','2018-05-28 18:05:54','2018-05-28 18:05:54'), -(575,1,'FCS_CURRENCY_SYMBOL','Currency symbol','$','readonly',520,'en_US','2018-06-13 19:53:14','2018-06-13 19:53:14'), -(576,1,'FCS_DEFAULT_LOCALE','Language','en_US','readonly',550,'en_US','2018-06-26 10:18:55','2018-06-26 10:18:55'), -(577,1,'FCS_FOODCOOPS_MAP_ENABLED','Show map with other foodcoops on home?','1','boolean',1280,'en_US','2019-02-11 22:25:36','2019-02-11 22:25:36'), -(578,1,'FCS_WEEKLY_PICKUP_DAY','Weekly pickup day','5','readonly',600,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(579,1,'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA','Sending of order lists: x days before pickup day','2','readonly',650,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(580,1,'FCS_ORDER_POSSIBLE_FOR_STOCK_PRODUCTS_IN_ORDERS_WITH_DELIVERY_RHYTHM','Allow weekly orders for stock products?','1','boolean',750,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(581,1,'FCS_SHOW_NON_STOCK_PRODUCTS_IN_INSTANT_ORDERS','Only show stock products in instant orders?','0','boolean',760,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(582,1,'FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES','Include stock products in invoices?','1','readonly',600,'en_US','2019-02-18 12:38:00','2019-02-18 12:38:00'), -(583,1,'FCS_REGISTRATION_NOTIFICATION_EMAILS','Who should be notified on new registrations?
Please separate multiple e-mail addresses with , (no space).
','','text',550,'en_US','2019-03-05 20:01:59','2019-03-05 20:01:59'), -(584,1,'FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED','Use self-service mode for stock products?
Online documentation
','0','boolean',3000,'en_US','2019-04-17 20:01:59','2019-04-17 20:01:59'), -(585,1,'FCS_APP_ADDITIONAL_DATA','Additional food-coop infos','','textarea',80,'en_US','2019-08-03 20:07:08','2019-08-03 20:07:08'), -(586,1,'FCS_SELF_SERVICE_MODE_TEST_MODE_ENABLED','Run self-service mode in test mode?
Does not add links to main menu and to stock products.
','0','boolean',3100,'en_US','2019-12-09 13:46:32','2019-12-09 13:46:32'), -(587,1,'FCS_CASHLESS_PAYMENT_ADD_TYPE','Type of adding the payments
How do the payment addings get into FoodCoopShop?
','list-upload','dropdown',1450,'en_US','2020-02-11 10:13:01','2020-02-11 10:13:01'), -(589,1,'FCS_FEEDBACK_TO_PRODUCTS_ENABLED','Are members allowed to write feedback to products?','0','boolean',3200,'en_US','2020-06-19 09:02:50','2020-06-19 09:02:50'), -(590,1,'FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY','Pickup day can be selected by member on order confirmation.','0','readonly',590,'en_US','2020-07-06 10:34:39','2020-07-06 10:34:39'), -(591,1,'FCS_SEND_INVOICES_TO_CUSTOMERS','Retail mode activated?.','0','readonly',580,'en_US','2020-10-29 10:06:39','2020-10-29 10:06:39'), -(592,1,'FCS_DEPOSIT_TAX_RATE','VAT for deposit','20.00','readonly',581,'en_US','2020-11-03 15:24:01','2020-11-03 15:24:01'), -(593,1,'FCS_INVOICE_HEADER_TEXT','Header text for invoices to members','','readonly',582,'en_US','2020-11-03 15:24:01','2020-11-03 15:24:01'), -(594,1,'FCS_MEMBER_FEE_PRODUCTS','Which products are used as member fee product?
The selected products are the basis for the column Member Fee in the members adminstration and are not shown in the turnover statistics.
','','multiple_dropdown',3300,'en_US','2020-12-20 19:26:16','2020-12-20 19:26:16'), -(595,1,'FCS_CHECK_CREDIT_BALANCE_LIMIT','Height of credit saldo when the reminder email is sent.','50','number',1450,'en_US','2021-01-19 11:23:39','2021-01-19 11:23:39'), -(596,1,'FCS_PURCHASE_PRICE_ENABLED','Enable input of purchase price?
The purchase price is the base for profit statistics and bill of delivery to manufacturers.
','0','readonly',587,'en_US','2021-05-10 11:27:43','2021-05-10 11:27:43'), -(597,1,'FCS_HELLO_CASH_API_ENABLED','Enable API to hellocash.at?
Invoices (cash and cashless) are generated by hellocash.at.
','0','readonly',583,'en_US','2021-07-07 10:55:08','2021-07-07 10:55:08'), -(598,1,'FCS_SAVE_STORAGE_LOCATION_FOR_PRODUCTS','Save storage location for products?
New button next to \"Orders - show order as pdf\"
','1','boolean',3210,'en_US','2021-08-02 11:28:34','2021-08-02 11:28:34'), -(599,1,'FCS_INSTAGRAM_URL','Instagram url for embedding in footer','','text',920,'en_US','2021-09-10 21:23:13','2021-09-10 21:23:13'), -(600,1,'FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY','Ordering products with delivery rhythm one or two weeks is only possible in the week before delivery.','0','boolean',3210,'en_US','2022-02-01 17:48:40','2022-02-01 17:48:40'), -(601,1,'FCS_INVOICE_NUMBER_PREFIX','Prefix for invoice numbers
Max. 6 chars incl. separator.
','','readonly',586,'en_US','2022-03-21 12:02:57','2022-03-21 12:02:57'), -(602,1,'FCS_TAX_BASED_ON_NET_INVOICE_SUM','Invoices for companies with fixed tax rate
Vat is calculated based on the sum of net price of the invoice.
','0','readonly',585,'en_US','2022-03-23 09:12:33','2022-03-23 09:12:33'), -(603,1,'FCS_NEWSLETTER_ENABLED','Newsletter enabled?
Shows newsletter checkbox on registration.
','0','boolean',3400,'en_US','2022-04-12 15:28:56','2022-04-12 15:28:56'), -(604,1,'FCS_USER_FEEDBACK_ENABLED','Member and manufacturer feedback enabled?
Members and manufacturers can write feedback.
','0','boolean',3500,'en_US','2022-07-19 14:39:36','2022-07-19 14:39:36'); -/*!40000 ALTER TABLE `fcs_configuration` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_cronjob_logs` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_cronjob_logs` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_cronjobs` DISABLE KEYS */; -INSERT INTO `fcs_cronjobs` VALUES -(1,'BackupDatabase','day',NULL,NULL,'04:00:00',1), -(2,'CheckCreditBalance','week',NULL,'Friday','22:30:00',1), -(3,'EmailOrderReminder','week',NULL,'Monday','18:00:00',1), -(4,'PickupReminder','week',NULL,'Monday','09:00:00',1), -(5,'SendInvoicesToManufacturers','month',11,NULL,'10:30:00',1), -(6,'SendOrderLists','day',NULL,NULL,'04:30:00',1), -(7,'SendInvoicesToCustomers','week',NULL,'Saturday','10:00:00',0), -(8,'SendDeliveryNotes','month',1,NULL,'18:00:00',0); -/*!40000 ALTER TABLE `fcs_cronjobs` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_customer` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_customer` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_deposits` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_deposits` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_feedbacks` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_feedbacks` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_images` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_images` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_invoice_taxes` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_invoice_taxes` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_invoices` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_invoices` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_manufacturer` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_manufacturer` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail_feedbacks` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail_feedbacks` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail_purchase_prices` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail_purchase_prices` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_order_detail_units` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_order_detail_units` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_pages` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_pages` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_payments` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_payments` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_pickup_days` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_pickup_days` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_product` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_product` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_product_attribute` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_product_attribute` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_product_attribute_combination` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_product_attribute_combination` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_purchase_prices` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_purchase_prices` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_sliders` DISABLE KEYS */; -INSERT INTO `fcs_sliders` VALUES -(1,'demo-slider.jpg',NULL,0,0,1); -/*!40000 ALTER TABLE `fcs_sliders` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_stock_available` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_stock_available` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_storage_locations` DISABLE KEYS */; -INSERT INTO `fcs_storage_locations` VALUES -(1,'No cooling',10), -(2,'Refrigerator',20), -(3,'Freezer',30); -/*!40000 ALTER TABLE `fcs_storage_locations` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_sync_domains` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_sync_domains` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_sync_products` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_sync_products` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_tax` DISABLE KEYS */; -INSERT INTO `fcs_tax` VALUES -(1,20.000,1,0), -(2,10.000,1,0), -(3,13.000,1,0); -/*!40000 ALTER TABLE `fcs_tax` ENABLE KEYS */; - -/*!40000 ALTER TABLE `fcs_units` DISABLE KEYS */; -/*!40000 ALTER TABLE `fcs_units` ENABLE KEYS */; - -/*!40000 ALTER TABLE `phinxlog` DISABLE KEYS */; -INSERT INTO `phinxlog` VALUES -(20200404145856,'RemoveV2Migrations','2020-04-04 15:01:08','2020-04-04 15:01:08',0), -(20200415073329,'ShowNewProductsOnHome','2020-04-15 07:41:58','2020-04-15 07:41:58',0), -(20200501192722,'EnableCashlessPaymentAddTypeConfiguration','2020-05-01 19:30:13','2020-05-01 19:30:13',0), -(20200618063024,'AddProductFeedback','2020-06-19 07:02:50','2020-06-19 07:02:50',0), -(20200703072605,'CustomerCanSelectPickupDay','2020-07-06 08:34:39','2020-07-06 08:34:39',0), -(20200831142250,'RemoveEmailLogTable','2020-08-31 15:10:25','2020-08-31 15:10:25',0), -(20200910091755,'AddMemberSettingUseCameraForMobileBarcodeScanning','2020-09-10 09:20:55','2020-09-10 09:20:55',0), -(20200925073919,'GermanIbanFix','2020-09-25 08:12:42','2020-09-25 08:12:42',0), -(20201017182431,'AdaptMinimalCreditBalance','2020-10-17 18:38:00','2020-10-17 18:38:00',0), -(20201029084931,'AddRetailMode','2020-10-29 09:06:39','2020-10-29 09:06:39',0), -(20201118084516,'AddRetailMode2','2020-11-18 08:47:37','2020-11-18 08:47:37',0), -(20201213120713,'AddRetailMode3','2020-12-13 12:14:01','2020-12-13 12:14:01',0), -(20201217101514,'SliderWithLink','2020-12-17 10:26:36','2020-12-17 10:26:37',0), -(20201220182015,'ImproveMemberFeeAdministration','2020-12-20 18:26:16','2020-12-20 18:26:16',0), -(20210119101923,'CheckCreditBalanceLimit','2021-01-19 10:23:39','2021-01-19 10:23:39',0), -(20210401071718,'RemoveCustomerGroupSetting','2021-04-01 07:18:49','2021-04-01 07:18:49',0), -(20210401082727,'CustomerActivateEmailCode','2021-04-01 08:29:17','2021-04-01 08:29:18',0), -(20210419084816,'BlogPostShowOnStartPageUntilDate','2021-04-19 09:41:18','2021-04-19 09:41:18',0), -(20210427144234,'RemoveOldMemberFeeSetting','2021-04-27 15:04:58','2021-04-27 15:04:58',0), -(20210504085123,'SaveTaxInOrderDetails','2021-05-04 09:10:09','2021-05-04 09:10:09',0), -(20210510080630,'EnablePurchasePrices','2021-05-10 09:27:43','2021-05-10 09:27:43',0), -(20210707083827,'AddRegistrierkasseApi','2021-07-07 08:55:08','2021-07-07 08:55:08',0), -(20210802090623,'AddStorageLocation','2021-08-02 09:28:34','2021-08-02 09:28:35',0), -(20210910191430,'Instagram','2021-09-10 19:23:13','2021-09-10 19:23:13',0), -(20210914071747,'DifferentPricesForCustomers','2021-09-16 05:50:07','2021-09-16 05:50:07',0), -(20210922154148,'RemoveUnusedQueueTable','2021-09-22 15:43:04','2021-09-22 15:43:04',0), -(20210923073422,'RemoveSettingShowNewProductsOnHome','2021-09-23 07:39:27','2021-09-23 07:39:27',0), -(20210923090820,'AllowNullAsPurchasePrice','2021-09-23 09:09:46','2021-09-23 09:09:46',0), -(20211028083847,'UseExistingBarcode','2021-10-28 08:44:56','2021-10-28 08:44:56',0), -(20211123095227,'DeactivateCheckCreditReminder','2021-11-23 10:01:00','2021-11-23 10:01:00',0), -(20211213081433,'ImproveCustomerNotifications','2021-12-13 09:24:39','2021-12-13 09:24:40',0), -(20211213081434,'ImproveCustomerNotifications','2021-12-13 10:44:50','2021-12-13 10:44:50',0), -(20211215184633,'ManufacturerSettingIncludeStockProductsOnOrderList','2021-12-15 18:50:55','2021-12-15 18:50:55',0), -(20211229194617,'AddIndizesForBetterPerformance','2021-12-29 19:55:34','2021-12-29 19:55:34',0), -(20220129082136,'SendDeliveryNotesEveryMonth','2022-01-29 08:31:56','2022-01-29 08:31:56',0), -(20220201163254,'OptionalDeliveryRhythmSettingOrderInWeekBeforeDelivery','2022-02-01 16:48:40','2022-02-01 16:48:40',0), -(20220321103059,'PrefixForInvoices','2022-03-21 11:02:57','2022-03-21 11:02:57',0), -(20220323075926,'TaxBasedOnNetInvoiceSum','2022-03-23 08:12:33','2022-03-23 08:12:33',0), -(20220407093247,'AddIsCompanyFieldForCustomer','2022-04-07 09:37:35','2022-04-07 09:37:35',0), -(20220412131842,'Newsletter','2022-04-12 13:28:56','2022-04-12 13:28:56',0), -(20220525092822,'BiggerQueuedJobDataField','2022-05-25 09:32:00','2022-05-25 09:32:00',0), -(20220620091755,'RemoveTimebasedCurrencyModule','2022-06-20 09:30:29','2022-06-20 09:30:29',0), -(20220717194215,'UserFeedback','2022-07-19 12:39:36','2022-07-19 12:39:36',0); -/*!40000 ALTER TABLE `phinxlog` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queue_phinxlog` DISABLE KEYS */; -INSERT INTO `queue_phinxlog` VALUES -(20150425180802,'Init','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20150511062806,'Fixmissing','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20150911132343,'ImprovementsForMysql','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20161319000000,'IncreaseDataSize','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20161319000001,'Priority','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20161319000002,'Rename','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20161319000003,'Processes','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20171013131845,'AlterQueuedJobs','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20171013133145,'Utf8mb4Fix','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20171019083500,'ColumnLength','2020-09-17 07:23:25','2020-09-17 07:23:25',0), -(20171019083501,'MigrationQueueNull','2020-09-17 07:23:25','2020-09-17 07:23:26',0), -(20171019083502,'MigrationQueueStatus','2020-09-17 07:23:26','2020-09-17 07:23:26',0), -(20171019083503,'MigrationQueueProcesses','2020-09-17 07:23:26','2020-09-17 07:23:26',0), -(20171019083505,'MigrationQueueProcessesIndex','2020-09-17 07:23:26','2020-09-17 07:23:26',0), -(20171019083506,'MigrationQueueProcessesKey','2020-09-17 07:23:26','2020-09-17 07:23:26',0), -(20191319000002,'MigrationQueueRename','2021-07-20 11:12:57','2021-07-20 11:12:57',0); -/*!40000 ALTER TABLE `queue_phinxlog` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queue_processes` DISABLE KEYS */; -/*!40000 ALTER TABLE `queue_processes` ENABLE KEYS */; - -/*!40000 ALTER TABLE `queued_jobs` DISABLE KEYS */; -/*!40000 ALTER TABLE `queued_jobs` ENABLE KEYS */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - diff --git a/config/sql/_installation/clean-db-structure.sql b/config/sql/_installation/clean-db-structure.sql deleted file mode 100644 index 97655aa0f7..0000000000 --- a/config/sql/_installation/clean-db-structure.sql +++ /dev/null @@ -1,706 +0,0 @@ - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -DROP TABLE IF EXISTS `fcs_action_logs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_action_logs` ( - `id` int NOT NULL AUTO_INCREMENT, - `type` varchar(255) NOT NULL DEFAULT '', - `customer_id` int unsigned NOT NULL DEFAULT '0', - `object_id` int unsigned NOT NULL DEFAULT '0', - `object_type` varchar(255) NOT NULL DEFAULT '', - `text` mediumtext NOT NULL, - `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_address`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_address` ( - `id_address` int unsigned NOT NULL AUTO_INCREMENT, - `id_customer` int unsigned NOT NULL DEFAULT '0', - `id_manufacturer` int unsigned NOT NULL DEFAULT '0', - `lastname` varchar(50) NOT NULL DEFAULT '', - `firstname` varchar(50) NOT NULL DEFAULT '', - `address1` varchar(128) NOT NULL DEFAULT '', - `address2` varchar(128) DEFAULT NULL, - `postcode` varchar(12) DEFAULT NULL, - `city` varchar(64) NOT NULL DEFAULT '', - `comment` mediumtext, - `phone` varchar(32) DEFAULT NULL, - `phone_mobile` varchar(32) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `date_add` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `date_upd` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id_address`), - KEY `address_customer` (`id_customer`), - KEY `id_manufacturer` (`id_manufacturer`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_attribute`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_attribute` ( - `id_attribute` int unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(128) DEFAULT NULL, - `can_be_used_as_unit` tinyint unsigned NOT NULL DEFAULT '0', - `active` int NOT NULL DEFAULT '1', - `created` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, - PRIMARY KEY (`id_attribute`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_barcodes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_barcodes` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `product_id` int unsigned NOT NULL DEFAULT '0', - `product_attribute_id` int unsigned NOT NULL DEFAULT '0', - `barcode` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `product_id` (`product_id`,`product_attribute_id`), - KEY `barcode` (`barcode`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_blog_posts`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_blog_posts` ( - `id_blog_post` int unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(150) NOT NULL, - `short_description` varchar(100) NOT NULL, - `content` longtext NOT NULL, - `id_customer` int unsigned NOT NULL DEFAULT '0', - `id_manufacturer` int unsigned DEFAULT NULL, - `is_private` int unsigned NOT NULL DEFAULT '0', - `active` int DEFAULT NULL, - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `modified` datetime DEFAULT NULL, - `show_on_start_page_until` date DEFAULT NULL, - PRIMARY KEY (`id_blog_post`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_cart_product_units`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_cart_product_units` ( - `id_cart_product` int unsigned NOT NULL, - `ordered_quantity_in_units` decimal(10,3) unsigned DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_cart_products`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_cart_products` ( - `id_cart_product` int unsigned NOT NULL AUTO_INCREMENT, - `id_cart` int unsigned NOT NULL DEFAULT '0', - `id_product` int unsigned NOT NULL DEFAULT '0', - `id_product_attribute` int unsigned NOT NULL DEFAULT '0', - `amount` int unsigned NOT NULL DEFAULT '0', - `created` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, - PRIMARY KEY (`id_cart_product`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_carts`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_carts` ( - `id_cart` int unsigned NOT NULL AUTO_INCREMENT, - `id_customer` int unsigned NOT NULL DEFAULT '0', - `cart_type` int unsigned NOT NULL DEFAULT '1', - `status` tinyint NOT NULL DEFAULT '1', - `created` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, - PRIMARY KEY (`id_cart`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_category`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_category` ( - `id_category` int unsigned NOT NULL AUTO_INCREMENT, - `id_parent` int unsigned DEFAULT '0', - `name` varchar(128) NOT NULL, - `description` mediumtext NOT NULL, - `nleft` int NOT NULL DEFAULT '0', - `nright` int NOT NULL DEFAULT '0', - `active` tinyint unsigned NOT NULL DEFAULT '0', - `created` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, - PRIMARY KEY (`id_category`), - KEY `category_parent` (`id_parent`), - KEY `nleftrightactive` (`nleft`,`nright`,`active`), - KEY `nright` (`nright`), - KEY `activenleft` (`active`,`nleft`), - KEY `activenright` (`active`,`nright`), - KEY `active` (`active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_category_product`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_category_product` ( - `id_category` int unsigned NOT NULL DEFAULT '0', - `id_product` int unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id_category`,`id_product`), - KEY `id_product` (`id_product`), - KEY `id_category` (`id_category`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_configuration`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_configuration` ( - `id_configuration` int unsigned NOT NULL AUTO_INCREMENT, - `active` tinyint(1) NOT NULL DEFAULT '0', - `name` varchar(254) NOT NULL DEFAULT '', - `text` mediumtext NOT NULL, - `value` mediumtext, - `type` varchar(20) NOT NULL DEFAULT '', - `position` int unsigned NOT NULL DEFAULT '0', - `locale` varchar(5) DEFAULT NULL, - `date_add` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `date_upd` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id_configuration`), - KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_cronjob_logs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_cronjob_logs` ( - `id` int NOT NULL AUTO_INCREMENT, - `cronjob_id` int unsigned NOT NULL, - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `success` tinyint unsigned NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_cronjobs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_cronjobs` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(255) NOT NULL, - `time_interval` varchar(50) NOT NULL, - `day_of_month` tinyint unsigned DEFAULT NULL, - `weekday` varchar(50) DEFAULT NULL, - `not_before_time` time NOT NULL, - `active` tinyint unsigned NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_customer`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_customer` ( - `id_customer` int unsigned NOT NULL AUTO_INCREMENT, - `id_default_group` int unsigned NOT NULL DEFAULT '1', - `is_company` tinyint NOT NULL DEFAULT '0', - `firstname` varchar(50) NOT NULL DEFAULT '', - `lastname` varchar(50) NOT NULL DEFAULT '', - `email` varchar(128) NOT NULL DEFAULT '', - `passwd` char(60) DEFAULT NULL, - `tmp_new_passwd` char(60) DEFAULT NULL, - `activate_new_password_code` varchar(12) DEFAULT NULL, - `auto_login_hash` varchar(40) DEFAULT NULL, - `email_order_reminder_enabled` tinyint unsigned NOT NULL DEFAULT '0', - `terms_of_use_accepted_date` date NOT NULL DEFAULT '1000-01-01', - `activate_email_code` varchar(12) DEFAULT NULL, - `active` tinyint unsigned NOT NULL DEFAULT '0', - `date_add` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `date_upd` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `use_camera_for_barcode_scanning` tinyint unsigned DEFAULT '0', - `user_id_registrierkasse` int unsigned DEFAULT '0', - `shopping_price` varchar(2) DEFAULT 'SP', - `check_credit_reminder_enabled` tinyint unsigned DEFAULT '1', - `invoices_per_email_enabled` tinyint unsigned DEFAULT '1', - `pickup_day_reminder_enabled` tinyint unsigned DEFAULT '1', - `credit_upload_reminder_enabled` tinyint unsigned DEFAULT '1', - `newsletter_enabled` tinyint unsigned DEFAULT '0', - PRIMARY KEY (`id_customer`), - KEY `customer_email` (`email`), - KEY `customer_login` (`email`,`passwd`), - KEY `id_customer_passwd` (`id_customer`,`passwd`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_deposits`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_deposits` ( - `id` int NOT NULL AUTO_INCREMENT, - `id_product` int unsigned NOT NULL DEFAULT '0', - `id_product_attribute` int unsigned NOT NULL DEFAULT '0', - `deposit` double NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_feedbacks`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_feedbacks` ( - `id` int NOT NULL AUTO_INCREMENT, - `customer_id` int DEFAULT NULL, - `text` text, - `approved` datetime NOT NULL DEFAULT '1970-01-01 00:00:00', - `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `privacy_type` tinyint DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_images`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_images` ( - `id_image` int unsigned NOT NULL AUTO_INCREMENT, - `id_product` int unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id_image`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_invoice_taxes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_invoice_taxes` ( - `id` int NOT NULL AUTO_INCREMENT, - `invoice_id` int unsigned NOT NULL DEFAULT '0', - `tax_rate` double(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax_excl` double(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax` double(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax_incl` double(20,6) NOT NULL DEFAULT '0.000000', - PRIMARY KEY (`id`), - KEY `invoice_id` (`invoice_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_invoices`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_invoices` ( - `id` int NOT NULL AUTO_INCREMENT, - `id_manufacturer` int unsigned NOT NULL DEFAULT '0', - `invoice_number` varchar(17) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0', - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `id_customer` int unsigned NOT NULL DEFAULT '0', - `paid_in_cash` tinyint unsigned DEFAULT '0', - `filename` varchar(512) NOT NULL DEFAULT '', - `email_status` varchar(30) DEFAULT NULL, - `cancellation_invoice_id` int unsigned DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_manufacturer`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_manufacturer` ( - `id_manufacturer` int unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(64) DEFAULT NULL, - `description` longtext, - `short_description` mediumtext, - `created` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, - `active` tinyint(1) NOT NULL DEFAULT '0', - `is_private` int unsigned NOT NULL DEFAULT '0', - `uid_number` varchar(30) DEFAULT NULL, - `additional_text_for_invoice` mediumtext, - `iban` varchar(22) DEFAULT NULL, - `bic` varchar(11) DEFAULT NULL, - `bank_name` varchar(255) DEFAULT NULL, - `firmenbuchnummer` varchar(20) DEFAULT NULL, - `firmengericht` varchar(150) DEFAULT NULL, - `aufsichtsbehoerde` varchar(150) DEFAULT NULL, - `kammer` varchar(150) DEFAULT NULL, - `homepage` varchar(255) DEFAULT NULL, - `id_customer` int unsigned DEFAULT NULL, - `variable_member_fee` int unsigned DEFAULT NULL, - `send_invoice` tinyint unsigned DEFAULT NULL, - `send_order_list` tinyint unsigned DEFAULT NULL, - `default_tax_id` int unsigned DEFAULT NULL, - `default_tax_id_purchase_price` int unsigned DEFAULT NULL, - `send_order_list_cc` varchar(512) DEFAULT NULL, - `send_instant_order_notification` tinyint unsigned DEFAULT NULL, - `send_ordered_product_deleted_notification` int unsigned DEFAULT NULL, - `send_ordered_product_price_changed_notification` int unsigned DEFAULT NULL, - `send_ordered_product_amount_changed_notification` int unsigned DEFAULT NULL, - `enabled_sync_domains` varchar(50) DEFAULT NULL, - `stock_management_enabled` tinyint unsigned NOT NULL DEFAULT '0', - `send_product_sold_out_limit_reached_for_manufacturer` tinyint unsigned NOT NULL DEFAULT '0', - `send_product_sold_out_limit_reached_for_contact_person` tinyint unsigned NOT NULL DEFAULT '0', - `no_delivery_days` mediumtext, - `include_stock_products_in_order_lists` tinyint unsigned NOT NULL DEFAULT '1', - `send_delivery_notes` tinyint unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id_manufacturer`), - KEY `stock_management_enabled` (`stock_management_enabled`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_order_detail`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_order_detail` ( - `id_order_detail` int unsigned NOT NULL AUTO_INCREMENT, - `product_id` int unsigned NOT NULL DEFAULT '0', - `product_attribute_id` int unsigned DEFAULT NULL, - `product_name` varchar(255) NOT NULL DEFAULT '', - `product_amount` int unsigned NOT NULL DEFAULT '0', - `total_price_tax_incl` decimal(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax_excl` decimal(20,6) NOT NULL DEFAULT '0.000000', - `tax_unit_amount` decimal(16,6) NOT NULL DEFAULT '0.000000', - `tax_total_amount` decimal(16,6) NOT NULL DEFAULT '0.000000', - `tax_rate` decimal(10,3) NOT NULL DEFAULT '0.000', - `deposit` decimal(10,2) NOT NULL DEFAULT '0.00', - `id_customer` int unsigned NOT NULL, - `id_invoice` int unsigned DEFAULT NULL, - `id_cart_product` int unsigned NOT NULL, - `order_state` tinyint unsigned NOT NULL, - `pickup_day` date NOT NULL, - `shopping_price` varchar(2) DEFAULT 'SP', - `created` datetime NOT NULL, - `modified` datetime NOT NULL, - PRIMARY KEY (`id_order_detail`), - KEY `product_id` (`product_id`), - KEY `product_attribute_id` (`product_attribute_id`), - KEY `id_customer` (`id_customer`), - KEY `pickup_day` (`pickup_day`), - KEY `created` (`created`), - KEY `order_state` (`order_state`), - KEY `product_name` (`product_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_order_detail_feedbacks`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_order_detail_feedbacks` ( - `id_order_detail` int unsigned NOT NULL DEFAULT '0', - `text` text NOT NULL, - `customer_id` int unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id_order_detail`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_order_detail_purchase_prices`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_order_detail_purchase_prices` ( - `id_order_detail` int unsigned NOT NULL, - `tax_rate` decimal(10,3) NOT NULL DEFAULT '0.000', - `total_price_tax_incl` decimal(20,6) NOT NULL DEFAULT '0.000000', - `total_price_tax_excl` decimal(20,6) NOT NULL DEFAULT '0.000000', - `tax_unit_amount` decimal(16,6) NOT NULL DEFAULT '0.000000', - `tax_total_amount` decimal(16,6) NOT NULL DEFAULT '0.000000', - PRIMARY KEY (`id_order_detail`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_order_detail_units`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_order_detail_units` ( - `id_order_detail` int NOT NULL DEFAULT '0', - `product_quantity_in_units` decimal(10,3) unsigned DEFAULT NULL, - `price_incl_per_unit` decimal(10,2) unsigned DEFAULT NULL, - `purchase_price_incl_per_unit` decimal(10,2) unsigned DEFAULT NULL, - `quantity_in_units` decimal(10,3) unsigned DEFAULT NULL, - `unit_name` varchar(50) NOT NULL DEFAULT '', - `unit_amount` int unsigned DEFAULT NULL, - `mark_as_saved` tinyint unsigned NOT NULL DEFAULT '0', - UNIQUE KEY `id_order_detail` (`id_order_detail`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_pages`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_pages` ( - `id_page` int unsigned NOT NULL AUTO_INCREMENT, - `title` varchar(128) NOT NULL, - `content` longtext NOT NULL, - `position` int unsigned NOT NULL DEFAULT '0', - `menu_type` varchar(255) NOT NULL DEFAULT 'header', - `active` tinyint NOT NULL DEFAULT '0', - `extern_url` varchar(255) NOT NULL DEFAULT '', - `id_customer` int unsigned NOT NULL DEFAULT '0', - `is_private` int unsigned NOT NULL DEFAULT '0', - `modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `full_width` tinyint unsigned NOT NULL DEFAULT '0', - `id_parent` int unsigned DEFAULT '0', - `lft` int NOT NULL DEFAULT '0', - `rght` int NOT NULL DEFAULT '0', - PRIMARY KEY (`id_page`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_payments`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_payments` ( - `id` int NOT NULL AUTO_INCREMENT, - `id_customer` int unsigned NOT NULL DEFAULT '0', - `id_manufacturer` int unsigned NOT NULL DEFAULT '0', - `type` varchar(20) NOT NULL DEFAULT 'product', - `amount` decimal(10,2) NOT NULL DEFAULT '0.00', - `text` varchar(255) NOT NULL DEFAULT '', - `date_add` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `date_changed` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `date_transaction_add` datetime DEFAULT NULL, - `transaction_text` mediumtext, - `invoice_id` int unsigned DEFAULT NULL, - `status` tinyint NOT NULL DEFAULT '1', - `approval` tinyint NOT NULL DEFAULT '0', - `approval_comment` mediumtext, - `changed_by` int unsigned NOT NULL DEFAULT '0', - `created_by` int unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_pickup_days`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_pickup_days` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `customer_id` int unsigned NOT NULL, - `pickup_day` date NOT NULL, - `comment` mediumtext, - `products_picked_up` tinyint unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - KEY `customer_id` (`customer_id`), - KEY `pickup_day` (`pickup_day`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_product`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_product` ( - `id_product` int unsigned NOT NULL AUTO_INCREMENT, - `id_manufacturer` int unsigned DEFAULT NULL, - `id_tax` int unsigned NOT NULL DEFAULT '0', - `id_storage_location` tinyint unsigned NOT NULL DEFAULT '0', - `price` decimal(20,6) NOT NULL DEFAULT '0.000000', - `name` mediumtext NOT NULL, - `description` longtext, - `description_short` mediumtext, - `unity` varchar(255) DEFAULT NULL, - `is_declaration_ok` tinyint unsigned NOT NULL DEFAULT '0', - `is_stock_product` tinyint unsigned NOT NULL DEFAULT '0', - `active` int NOT NULL DEFAULT '0', - `delivery_rhythm_type` varchar(10) NOT NULL DEFAULT 'week', - `delivery_rhythm_count` tinyint NOT NULL DEFAULT '1', - `delivery_rhythm_first_delivery_day` date DEFAULT NULL, - `delivery_rhythm_order_possible_until` date DEFAULT NULL, - `delivery_rhythm_send_order_list_weekday` int unsigned DEFAULT NULL, - `delivery_rhythm_send_order_list_day` date DEFAULT NULL, - `created` datetime DEFAULT NULL, - `modified` datetime DEFAULT NULL, - PRIMARY KEY (`id_product`), - KEY `product_manufacturer` (`id_manufacturer`,`id_product`), - KEY `id_manufacturer` (`id_manufacturer`), - KEY `is_stock_product` (`is_stock_product`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_product_attribute`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_product_attribute` ( - `id_product_attribute` int unsigned NOT NULL AUTO_INCREMENT, - `id_product` int unsigned NOT NULL DEFAULT '0', - `price` decimal(20,6) NOT NULL DEFAULT '0.000000', - `default_on` tinyint unsigned DEFAULT NULL, - PRIMARY KEY (`id_product_attribute`), - KEY `product_attribute_product` (`id_product`), - KEY `id_product_id_product_attribute` (`id_product_attribute`,`id_product`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_product_attribute_combination`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_product_attribute_combination` ( - `id_attribute` int unsigned NOT NULL DEFAULT '0', - `id_product_attribute` int unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id_attribute`,`id_product_attribute`), - KEY `id_product_attribute` (`id_product_attribute`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_purchase_prices`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_purchase_prices` ( - `id` int unsigned NOT NULL AUTO_INCREMENT, - `product_id` int unsigned NOT NULL DEFAULT '0', - `product_attribute_id` int unsigned NOT NULL DEFAULT '0', - `tax_id` int unsigned DEFAULT '0', - `price` decimal(20,6) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `product_id` (`product_id`,`product_attribute_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_sliders`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_sliders` ( - `id_slider` int unsigned NOT NULL AUTO_INCREMENT, - `image` varchar(255) DEFAULT NULL, - `link` varchar(999) DEFAULT NULL, - `is_private` int unsigned NOT NULL DEFAULT '0', - `position` int unsigned NOT NULL DEFAULT '0', - `active` tinyint NOT NULL DEFAULT '0', - PRIMARY KEY (`id_slider`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_stock_available`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_stock_available` ( - `id_stock_available` int unsigned NOT NULL AUTO_INCREMENT, - `id_product` int unsigned NOT NULL DEFAULT '0', - `id_product_attribute` int unsigned NOT NULL DEFAULT '0', - `quantity` int NOT NULL DEFAULT '0', - `quantity_limit` int NOT NULL DEFAULT '0', - `sold_out_limit` int DEFAULT NULL, - `always_available` tinyint unsigned NOT NULL DEFAULT '1', - `default_quantity_after_sending_order_lists` int unsigned DEFAULT NULL, - PRIMARY KEY (`id_stock_available`), - UNIQUE KEY `product_sqlstock` (`id_product`,`id_product_attribute`), - KEY `id_product` (`id_product`), - KEY `id_product_attribute` (`id_product_attribute`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_storage_locations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_storage_locations` ( - `id` int NOT NULL AUTO_INCREMENT, - `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `rank` tinyint unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_sync_domains`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_sync_domains` ( - `id` int NOT NULL AUTO_INCREMENT, - `domain` varchar(128) NOT NULL DEFAULT '', - `active` tinyint NOT NULL DEFAULT '1', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_sync_products`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_sync_products` ( - `id` int NOT NULL AUTO_INCREMENT, - `sync_domain_id` int unsigned NOT NULL DEFAULT '0', - `local_product_id` int unsigned NOT NULL DEFAULT '0', - `remote_product_id` int unsigned NOT NULL DEFAULT '0', - `local_product_attribute_id` int unsigned NOT NULL DEFAULT '0', - `remote_product_attribute_id` int unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_tax`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_tax` ( - `id_tax` int unsigned NOT NULL AUTO_INCREMENT, - `rate` decimal(10,3) NOT NULL DEFAULT '0.000', - `active` tinyint unsigned NOT NULL DEFAULT '1', - `deleted` tinyint unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id_tax`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `fcs_units`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `fcs_units` ( - `id` int NOT NULL AUTO_INCREMENT, - `id_product` int unsigned DEFAULT NULL, - `id_product_attribute` int unsigned DEFAULT NULL, - `price_incl_per_unit` decimal(10,2) unsigned DEFAULT NULL, - `purchase_price_incl_per_unit` decimal(10,2) unsigned DEFAULT NULL, - `name` varchar(50) NOT NULL DEFAULT '', - `amount` int unsigned DEFAULT NULL, - `price_per_unit_enabled` tinyint NOT NULL DEFAULT '0', - `quantity_in_units` decimal(10,3) unsigned DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `id_product` (`id_product`,`id_product_attribute`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `phinxlog`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `phinxlog` ( - `version` bigint NOT NULL, - `migration_name` varchar(100) DEFAULT NULL, - `start_time` timestamp NULL DEFAULT NULL, - `end_time` timestamp NULL DEFAULT NULL, - `breakpoint` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`version`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `queue_phinxlog`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `queue_phinxlog` ( - `version` bigint NOT NULL, - `migration_name` varchar(100) DEFAULT NULL, - `start_time` timestamp NULL DEFAULT NULL, - `end_time` timestamp NULL DEFAULT NULL, - `breakpoint` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`version`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `queue_processes`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `queue_processes` ( - `id` int NOT NULL AUTO_INCREMENT, - `pid` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `created` datetime NOT NULL, - `modified` datetime NOT NULL, - `terminate` tinyint(1) NOT NULL DEFAULT '0', - `server` varchar(90) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `workerkey` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `workerkey` (`workerkey`), - UNIQUE KEY `pid` (`pid`,`server`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `queued_jobs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `queued_jobs` ( - `id` int NOT NULL AUTO_INCREMENT, - `job_task` varchar(90) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, - `data` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - `job_group` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `reference` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `created` datetime NOT NULL, - `notbefore` datetime DEFAULT NULL, - `fetched` datetime DEFAULT NULL, - `completed` datetime DEFAULT NULL, - `progress` float DEFAULT NULL, - `failed` int NOT NULL DEFAULT '0', - `failure_message` text, - `workerkey` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `priority` int NOT NULL DEFAULT '5', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - diff --git a/devtools/CHANGELOG-v3.md b/devtools/CHANGELOG-v3.md new file mode 100644 index 0000000000..7c587e88aa --- /dev/null +++ b/devtools/CHANGELOG-v3.md @@ -0,0 +1,235 @@ + +# v3.4.2 + +### Security update +Fixed security issue in CKEditor. + +Datum: 18.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/16) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.4.1...v3.4.2) + +# v3.4.1 + +### Bugfix +- Fix error on creating build. [PR#802](https://github.com/foodcoopshop/foodcoopshop/pull/802) + +Datum: 06.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/16) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.4.0...v3.4.1) + +# v3.4.0 + +### Herzlichen Dank an alle beteiligten Personen +* [Confuset](https://github.com/Confuset) +* [mrothauer](https://github.com/mrothauer) +* [ocapito](https://github.com/ocapito) +* [pabneukistl](https://github.com/pabneukistl) + +### Verbesserungen +- Die Summe im Warenkorb ist jetzt in Warenwert, Pfandsumme und Gesamtbetrag aufgesplittet. [I#636](https://github.com/foodcoopshop/foodcoopshop/issues/636) / [PR#699](https://github.com/foodcoopshop/foodcoopshop/pull/699) +- Der Instagram-Account kann nun in den Einstellungen angegeben werden, außerdem ist der Bereich für die Social-Media-Links im Footer neu gestaltet. [I#642](https://github.com/foodcoopshop/foodcoopshop/issues/642) / [PR#706](https://github.com/foodcoopshop/foodcoopshop/pull/706) +- Im Selbstbedienungs-Modus werden die Produkte erst nach Suche, Scannen oder Kategorie-Auswahl angezeigt. Außerdem steht im Kategorien-Dropdown jetzt die Produkt-Anzahl. [I#703](https://github.com/foodcoopshop/foodcoopshop/issues/703) / [PR#704](https://github.com/foodcoopshop/foodcoopshop/pull/704) +- Für Admins und Superadmins ist es jetzt möglich, über den Selbstbedienungs-Modus für andere Mitglieder zu bestellen. Der Vorteil gegenüber der Sofort-Bestellung besteht darin, dass das tatsächlich entnommene Gewicht direkt beim Bestellen eingegeben werden kann. [I#703](https://github.com/foodcoopshop/foodcoopshop/issues/703) / [PR#718](https://github.com/foodcoopshop/foodcoopshop/pull/718) +- Beim Bild-Upload ist jetzt neben dem JPG-Format auch PNG und GIF verwendbar. [I#702](https://github.com/foodcoopshop/foodcoopshop/issues/702) / [PR#720](https://github.com/foodcoopshop/foodcoopshop/pull/720) +- Selbstbedienungs-Modus: Bereits bestehende Barcodes (EAN-13) können nun Produkten und Varianten zugewiesen und zum Scannen verwendet werden. [I#707](https://github.com/foodcoopshop/foodcoopshop/issues/706) / [PR#729](https://github.com/foodcoopshop/foodcoopshop/pull/729) +- Der CSV-Upload für die Guthaben-Aufladungen unterstützt jetzt auch die Volksbank. [I#732](https://github.com/foodcoopshop/foodcoopshop/issues/732) / [PR#733](https://github.com/foodcoopshop/foodcoopshop/pull/733) +- Der zweiwöchige Lieferrhythmus kann jetzt so eingestellt werden, dass die Produkte nur noch in der Woche vor der Lieferung bestellt werden können. In der anderen Woche sind bei aktivierter Einstellung die Produkte beim Bestellen zwar sichtbar, aber nicht bestellbar. Auch bei einem vorgezogenen Bestellschluss, kann nur mehr in der Woche vor der Lieferung bestellt werden (gilt für ein- und zweiwöchig). Alle anderen Lieferrhythmen sind von der neuen Einstellung nicht betroffen. [I#716](https://github.com/foodcoopshop/foodcoopshop/issues/716) / [PR#734](https://github.com/foodcoopshop/foodcoopshop/pull/734) [C](https://github.com/foodcoopshop/foodcoopshop/commit/3e375b05e301c478b6dd0ecd34fdb969e20645bf) [PR#792](https://github.com/foodcoopshop/foodcoopshop/pull/792) [PR#793](https://github.com/foodcoopshop/foodcoopshop/pull/793) +- Im neuen Bereich "Benachrichtigungen" im User-Profil kann der User selbst folgende Benachrichtigungs-E-Mails deaktivieren: Guthaben-Erinnerungs-Mail, Rechnung, Abholtag-Erinnerung, Überweisung wurde ins Guthaben-System übernommen. [I#739](https://github.com/foodcoopshop/foodcoopshop/issues/739) / [I#752](https://github.com/foodcoopshop/foodcoopshop/issues/752) / [PR#740](https://github.com/foodcoopshop/foodcoopshop/pull/740) / [PR#754](https://github.com/foodcoopshop/foodcoopshop/pull/754) +- Als "abgeholt" markierte Bestellungen werden nun im unteren Bereich der Liste "Admin-Bereich / Bestellungen" gruppiert angezeigt. Damit bleibt die Liste auch bei vielen Bestellungen übersichtlich. [C](https://github.com/foodcoopshop/foodcoopshop/commit/cc53502b57b5d42e6ba5f01b2bb9d91383cca253) +- Statusmeldungen (z.B. "Dein Profil wurde erfolgreich geändert.") schließen sich jetzt automatisch nach 5 Sekunden. Fehlermeldungen müssen nach wie vor manuell geschlossen werden. [PR#746](https://github.com/foodcoopshop/foodcoopshop/pull/746) +- Die 🥕 Karotte für neue Mitglieder wird angezeigt, wenn Bestellungen für maximal 2 Abholtage vorhanden sind. [C](https://github.com/foodcoopshop/foodcoopshop/commit/6fe59562a5e96813425a138e27de57d8fb482135) +- Neue Hersteller-Einstellung: "Sollen die Bestelllisten auch Lagerprodukte enthalten?". [I#756](https://github.com/foodcoopshop/foodcoopshop/issues/756) / [PR#758](https://github.com/foodcoopshop/foodcoopshop/pull/758) +- Beim Ändern des Abholtages werden Mitglieder jetzt nicht mehr automatisch per E-Mail benachrichtigt. Die Benachrichtigung kann aber durch eine Checkbox aktiviert werden. [I#787](https://github.com/foodcoopshop/foodcoopshop/issues/787) / [PR#788](https://github.com/foodcoopshop/foodcoopshop/pull/788) + +### Neue Funktionen für den [Einzelhandels-Modus](https://foodcoopshop.github.io/de/dorfladen-online.html) +- Im Selbstbedienungs-Modus wird jetzt nach jedem Einkauf automatisch die Rechnung erstellt. Sie wird auch automatisch gedruckt, wenn die Funktion "Rechnungen per E-Mail erhalten" deaktiviert wurde. [I#696](https://github.com/foodcoopshop/foodcoopshop/issues/696) / [PR#697](https://github.com/foodcoopshop/foodcoopshop/pull/697) +- Die Rechnungen werden nun automatisch gedruckt, bar bezahlt wurde und die Funktion "Rechnungen per E-Mail erhalten" deaktiviert ist. [I#698](https://github.com/foodcoopshop/foodcoopshop/issues/698) / [PR#769](https://github.com/foodcoopshop/foodcoopshop/pull/769) +- Kunden haben jetzt im neuen Menüpunkt "Meine Rechnungen" eine schöne Rechnungs-Übersicht mit Download-Funktion. [I#646](https://github.com/foodcoopshop/foodcoopshop/issues/646) / [PR#705](https://github.com/foodcoopshop/foodcoopshop/pull/705) +- Es gibt nun eine neue Kunden-Einstellung, die Bestellungen zu Einkaufspreisen (für Eigenverbrauch) oder zu Nullpreisen (Vorbestellung von Produkten, die später nochmal verkauft werden) ermöglicht. [I#672](https://github.com/foodcoopshop/foodcoopshop/issues/672) / [PR#712](https://github.com/foodcoopshop/foodcoopshop/pull/712) +- Falls die Funktion zum Erfassen der Einkaufspreise aktiviert ist, können Hersteller nun folgende Daten der eigenen Produkte nicht mehr ändern: Einkaufspreis, Verkaufspreis, Pfand, Steuer. Außerdem wird auf den Bestelllisten der Preis nicht mehr angezeigt. [I#710](https://github.com/foodcoopshop/foodcoopshop/issues/710) [I#711](https://github.com/foodcoopshop/foodcoopshop/issues/711) / [PR#714](https://github.com/foodcoopshop/foodcoopshop/pull/714) [PR#715](https://github.com/foodcoopshop/foodcoopshop/pull/715) +- Weiters werden Produkte bzw. Varianten ohne Einkaufspreis nicht mehr im Produktkatalog angezeigt und können so nicht bestellt werden. [I#745](https://github.com/foodcoopshop/foodcoopshop/issues/745) / [PR#748](https://github.com/foodcoopshop/foodcoopshop/pull/748) +- Die Verkaufspreise können jetzt auch auf Basis eines Aufschlages automatisch berechnet werden. [I#721](https://github.com/foodcoopshop/foodcoopshop/issues/721) / [PR#722](https://github.com/foodcoopshop/foodcoopshop/pull/722) +- Der Aufschlag, also die Differenz zwischen Netto-Einkaufpreis und Netto-Verkaufspreis wird im Admin-Bereich jetzt als Prozent- und Euro-Betrag angezeigt. [I#723](https://github.com/foodcoopshop/foodcoopshop/issues/723) / [PR#736](https://github.com/foodcoopshop/foodcoopshop/pull/736) +- Möglichkeit zum Erstellen eines Lieferscheines für die Hersteller. Die Datei (Excel-Format) enthält alle notwendigen Daten für die Erstellung einer Rechnung an den Betreiber (u.a. die Einkaufspreise). [I#627](https://github.com/foodcoopshop/foodcoopshop/issues/627) / [PR#738](https://github.com/foodcoopshop/foodcoopshop/pull/738) +- Diese Lieferscheine werden ab sofort auch automatisch 1x monatlich (immer am 1.) an die Hersteller versendet. Das Hakerl in den Hersteller-Einstellungen muss dafür aktiviert werden. [I#772](https://github.com/foodcoopshop/foodcoopshop/issues/772) / [PR#789](https://github.com/foodcoopshop/foodcoopshop/pull/789) +- Optimierungen für Barverkäufe. [I#742](https://github.com/foodcoopshop/foodcoopshop/issues/742) / [PR#743](https://github.com/foodcoopshop/foodcoopshop/pull/743) / [PR#747](https://github.com/foodcoopshop/foodcoopshop/pull/747) +- Gutscheine lassen sich nun anhand [dieser Anleitung](https://foodcoopshop.github.io/de/dorfladen-online-gutscheine.html) super im System abbilden. [I#750](https://github.com/foodcoopshop/foodcoopshop/issues/750) / [PR#762](https://github.com/foodcoopshop/foodcoopshop/pull/762) + +### Bugfixes / Updates / Performance +- Bei Installation mit vielen Produkten (> 500) waren Frontend-Seiten mit Produktlisten (Kategorie-Detailseite und Hersteller-Profil) sehr langsam. Jetzt laufen diese deutlich schneller. [I#700](https://github.com/foodcoopshop/foodcoopshop/issues/700) / [PR#764](https://github.com/foodcoopshop/foodcoopshop/pull/764) +- Fehlerhafte SQL-Abfrage mit MariaDB beim Erstellen der Bestelllisten. [I#724](https://github.com/foodcoopshop/foodcoopshop/issues/724) / [PR#726](https://github.com/foodcoopshop/foodcoopshop/pull/726) +- Die Sortierung von Mitgliedern / Herstellern funktioniert jetzt systemweit (z.B. in Admin-Bereich / Bestellungen) auch mit beginnenden Umlauten bzw. Kleinbuchstaben. [C1](5edbc970cc688f7a116ffba04170b391cc5a2e77) / [C2](aa12d4d18d14834dc82a84aa0b8dc068b1356f6d) +- CakePHP Update v4.3. [I#728](https://github.com/foodcoopshop/foodcoopshop/issues/728) / [PR#731](https://github.com/foodcoopshop/foodcoopshop/pull/731) +- BackupDatabaseShell now uses defined port. [PR#749](https://github.com/foodcoopshop/foodcoopshop/pull/749) + +Datum: 06.03.2022 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/16) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.3.0...v3.4.0) + +# v3.3.0 + +### Herzlichen Dank an alle beteiligten Personen +* [mrothauer](https://github.com/mrothauer) + +### Neue Funktionen +- Pfand-Rückgaben für Mitglieder können nun auch dann eingegeben werden, auch wenn das Mitglied in der aktuellen Woche nicht bestellt hat. Der Button "Pfand-Rückgabe" wird immer angezeigt, das Mitglied kann dann aus einer Dropdown-Liste ausgewählt werden. [I#654](https://github.com/foodcoopshop/foodcoopshop/issues/654) / [PR#655](https://github.com/foodcoopshop/foodcoopshop/pull/655) +- Produkte können jetzt auch einem Lagerort zugewiesen werden (z.B. Keine Kühlung, Kühlschrank, Tiefkühler). In der Liste "Bestellungen als PDF" werden die Produkte entsprechend sortiert angezeigt. [I#662](https://github.com/foodcoopshop/foodcoopshop/issues/662) / [PR#690](https://github.com/foodcoopshop/foodcoopshop/pull/690) +- Das Frontend wurde optisch aufgepeppt: Blog-Artikel, Hauptmenü, das mobile Menü, die Produkt-Liste und der Footer sind nun frischer. Weiters ist die Haupt-Schrift etwas größer und die Fett-Schrift dezenter. [I#643](https://github.com/foodcoopshop/foodcoopshop/issues/643) / [PR#648](https://github.com/foodcoopshop/foodcoopshop/pull/648) + +### Verbesserungen +- Bei der Produktbeschreibung wird jetzt ein Hinweis angezeigt, falls bereits offene Bestellungen vorhanden sind. Irrtümliche doppelte Bestellungen werden so vermieden. [I#681](https://github.com/foodcoopshop/foodcoopshop/issues/681) / [PR#694](https://github.com/foodcoopshop/foodcoopshop/pull/694) +- Bei Produkten ohne Bild wird jetzt das Hersteller-Logo angezeigt. Falls kein Hersteller-Logo vorhanden ist, wird das definierte Standard-Bild anzeigt. [I#490](https://github.com/foodcoopshop/foodcoopshop/issues/490) / [PR#693](https://github.com/foodcoopshop/foodcoopshop/pull/693) +- Beim Erstellen eines neuen Blog-Artikels kann nun angegeben werden, wie lange er auf der Startseite angezeigt werden soll. Danach verschwindet er automatisch. [I#601](https://github.com/foodcoopshop/foodcoopshop/issues/601) / [PR#664](https://github.com/foodcoopshop/foodcoopshop/pull/664) +- Beim Registrieren mit automatischer Aktivierung muss ab sofort die E-Mail-Adresse bestätigt werden, sonst bleibt der neue User inaktiv. [I#656](https://github.com/foodcoopshop/foodcoopshop/issues/656) / [PR#657](https://github.com/foodcoopshop/foodcoopshop/pull/657) +- Das Bestelldatum wird nun im Tooltip über dem Bestellstatus-Icon angezeigt. [I#652](https://github.com/foodcoopshop/foodcoopshop/issues/652) / [PR#653](https://github.com/foodcoopshop/foodcoopshop/pull/653) +- Alle Formulare sind jetzt noch besser gegen potenzielle Angriffe abgesichert. [I#659](https://github.com/foodcoopshop/foodcoopshop/issues/659) / [PR#661](https://github.com/foodcoopshop/foodcoopshop/pull/661) + +### Neue Funktionen für den [Einzelhandels-Modus](https://foodcoopshop.github.io/de/dorfladen-online.html) +- Nahtlose Integration der Registrierkasse HelloCash für den Einzelhandels-Modus. [Zur Online-Doku](https://foodcoopshop.github.io/de/registrierkasse-hello-cash.html). [I#683](https://github.com/foodcoopshop/foodcoopshop/issues/683) / [PR#686](https://github.com/foodcoopshop/foodcoopshop/pull/686) / [I#687](https://github.com/foodcoopshop/foodcoopshop/issues/687) / [PR#691](https://github.com/foodcoopshop/foodcoopshop/pull/691) +- Für Produkte kann nun auch der Einkaufspreis und dessen Umsatzsteuer angegeben werden. Diese Daten werden dann bei den Bestellungen mitgespeichert und liefern so die Datengrundlage für die neue Gewinnermittlung. [I#660](https://github.com/foodcoopshop/foodcoopshop/issues/660) / [PR#680](https://github.com/foodcoopshop/foodcoopshop/pull/680) / [I#671](https://github.com/foodcoopshop/foodcoopshop/issues/671) / [PR#692](https://github.com/foodcoopshop/foodcoopshop/pull/692) + +### Bugfixes +- Guthaben-Aufladungen mit CSV-Upload: Wenn ein Mitglied nicht ausgewählt wurde und man speichern wollte, wurde nicht die Fehlermeldung beim Mitglied angezeigt, sondern die allgemeine Fehlerseite. [I#677](https://github.com/foodcoopshop/foodcoopshop/issues/677) / [PR#678](https://github.com/foodcoopshop/foodcoopshop/pull/678) +- Mehrwöchiger Lieferrhythmus für Produkte mit Standard-Bestellschluss Montag und vorgezogener Bestellschluss Samstag wurde falsch berechnet. [PR#701](https://github.com/foodcoopshop/foodcoopshop/pull/701) + +### Code Cleaning +- Die alte Funktion zum Verwalten der Mitgliedbeiträge wurde entfernt. [Anleitung zum Umstellen auf das neue System](https://foodcoopshop.github.io/de/mitgliedsbeitraege.html). [I#666](https://github.com/foodcoopshop/foodcoopshop/issues/666) / [PR#667](https://github.com/foodcoopshop/foodcoopshop/pull/667) +- Sauberere SQL-Statements durch Verwendung von QueryExpression. [I#644](https://github.com/foodcoopshop/foodcoopshop/issues/644) / [PR#645](https://github.com/foodcoopshop/foodcoopshop/pull/645) + +Datum: 06.09.2021 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/15) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.2.2...v3.3.0) + +# v3.2.2 + +### Security Fix +* Das Registrierungsformular und das Formular zum Bearbeiten des User-Profils sind nun besser abgesichert. + +Datum: 12.04.2021 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.2.1...v3.2.2) + +# v3.2.1 + +### Bugfixes +* Wenn ein Hersteller die eigenen Einstellungen speichert, wird die Ansprechperson jetzt nicht mehr gelöscht. +* Layout-Fix im Overlay für die Produkt-Beschreibung. +* Kamera-Icon wurde auf der Login-Seite für den Selbstbedienung-Modus auf Smartphones nicht angezeigt. +* Wenn die Erstinstallation <= v3.0 war, müssen [zwei Migrations manuell ausgeführt werden](https://foodcoopshop.github.io/en/migration-guide.html). + +Datum: 23.03.2021 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.2.0...v3.2.1) + +# v3.2.0 + +### Herzlichen Dank an alle beteiligten Personen +* [AndreasEgger](https://github.com/AndreasEgger) +* [mantensteiner](https://github.com/mantensteiner) +* [markuskoban](https://github.com/markuskoban) +* [mrothauer](https://github.com/mrothauer) +* [reicharm](https://github.com/reicharm) + +### Neue Funktionen +- Das neue Modul zur Erstellung von Kunden-Rechnungen ermöglicht die Verwendung der Software im Einzelhandel. [Zur Online-Doku](https://foodcoopshop.github.io/de/dorfladen-online.html). [I#572](https://github.com/foodcoopshop/foodcoopshop/issues/572) / [PR#580](https://github.com/foodcoopshop/foodcoopshop/pull/580) / [PR#584](https://github.com/foodcoopshop/foodcoopshop/pull/584) / [PR#599](https://github.com/foodcoopshop/foodcoopshop/pull/599) +- Die Verwaltung der Mitgliedsbeiträge ist nun stark vereinfacht. [Zur Online-Doku](https://foodcoopshop.github.io/de/mitgliedsbeitraege.html). [I#471](https://github.com/foodcoopshop/foodcoopshop/issues/471) / [PR#608](https://github.com/foodcoopshop/foodcoopshop/pull/608) +- 📷 Beim Einkaufen im Selbstbedienungs-Modus kann man nun direkt mit der Smartphone-Kamera (ganz ohne App) die Barcodes scannen. [I#557](https://github.com/foodcoopshop/foodcoopshop/issues/557) / [PR#563](https://github.com/foodcoopshop/foodcoopshop/pull/563) +- Die stark verbesserte Pfand-Übersicht bringt endlich Licht 💡 in den Pfand-Dschungel 🐵, der sich bei manchen Initiativen über die Jahre ergeben hat. [I#570](https://github.com/foodcoopshop/foodcoopshop/issues/570) / [PR#571](https://github.com/foodcoopshop/foodcoopshop/pull/571) +- ☑ Beim Kontrollieren der Bestellungen bleiben die Produkte jetzt angehakt, wenn z.B. Gewicht oder Preis geändert wird. Wenn die Hakerl nicht wieder entfernt werden, sind sie nach 24 Stunden automatisch weg. [I#616](https://github.com/foodcoopshop/foodcoopshop/issues/616) / [PR#617](https://github.com/foodcoopshop/foodcoopshop/pull/617) +- Das eingestellte Guthaben-Limit kann nun beim normalen Bestellen nicht mehr unterschritten werden. Bei Sofort-Bestellungen und Preis- bzw. Gewichtsanpassungen ist dies aber weiterhin möglich. [I#555](https://github.com/foodcoopshop/foodcoopshop/issues/555) / [PR#574](https://github.com/foodcoopshop/foodcoopshop/pull/574) / [PR#603](https://github.com/foodcoopshop/foodcoopshop/pull/603) / [PR#635](https://github.com/foodcoopshop/foodcoopshop/pull/635) +- Die Guthaben-Höhe, ab der die Guthaben-Erinnerungsmail versendet wird, kann nun individuell eingestellt werden. Eine Erhöhung auf z.B. 50 € ist für Initiativen sinnvoll, die den CSV-Upload verwenden. [I#621](https://github.com/foodcoopshop/foodcoopshop/issues/621) / [PR#622](https://github.com/foodcoopshop/foodcoopshop/pull/622) +- Das individuelle Farbschema wird jetzt auch im Admin-Bereich angewendet. [I#613](https://github.com/foodcoopshop/foodcoopshop/issues/613) / [PR#630](https://github.com/foodcoopshop/foodcoopshop/pull/630) + +### Kleinere Verbesserungen +- Es gibt neue Lieferrhythmen: "jeder 2., 3. bzw. 4. Freitag im Monat" [I#581](https://github.com/foodcoopshop/foodcoopshop/issues/581) / [PR#582](https://github.com/foodcoopshop/foodcoopshop/pull/582) / Validierung: [PR#624](https://github.com/foodcoopshop/foodcoopshop/pull/624) +- Slideshow-Bilder auf der Startseite können jetzt verlinkt und außerdem auch "nur für Mitglieder" angezeigt werden. [I#600](https://github.com/foodcoopshop/foodcoopshop/issues/600) / [PR#606](https://github.com/foodcoopshop/foodcoopshop/pull/606) +- Die automatisch versendeten E-Mails bei Preis- und Gewichtsanpassungen von bestellten Produkten können nun global abgestellt werden. [I#576](https://github.com/foodcoopshop/foodcoopshop/issues/576) / [PR#577](https://github.com/foodcoopshop/foodcoopshop/pull/577) +- Fehlerhafte Gewichtsänderungen (z.B. 700 kg statt 700 g) können nun nicht mehr getätigt werden. [I#590](https://github.com/foodcoopshop/foodcoopshop/issues/590) / [PR#593](https://github.com/foodcoopshop/foodcoopshop/pull/593) +- Beim Ändern des Abgeholt-Status wird nun überprüft, ob das Gewicht für alle Produkte eingetragen wurde. [I#614](https://github.com/foodcoopshop/foodcoopshop/issues/614) / [PR#615](https://github.com/foodcoopshop/foodcoopshop/pull/615) +- Zur besseren Übersicht wird das "Bestellbar bis"-Datum jetzt bei jedem Produkt angezeigt. Außer bei Produkten mit wöchentlichem Lieferrhythmus und Standard-Bestellschluss. [I#585](https://github.com/foodcoopshop/foodcoopshop/issues/585) / [PR#594](https://github.com/foodcoopshop/foodcoopshop/pull/594) +- Produktbilder im Hochformat werden jetzt in der Lightbox besser dargestellt. [I#579](https://github.com/foodcoopshop/foodcoopshop/issues/579) / [PR#596](https://github.com/foodcoopshop/foodcoopshop/pull/596) +- Möglichkeit zur kompletten Deaktivierung des Pfand-Systems. [I#604](https://github.com/foodcoopshop/foodcoopshop/issues/604) / [PR#607](https://github.com/foodcoopshop/foodcoopshop/pull/607) +- Es wird nun ein Cookie-Banner eingeblendet und die Datenschutzerklärung ist wieder aktuell bezüglich der verwendeten Cookies. [I#619](https://github.com/foodcoopshop/foodcoopshop/issues/619) / [PR#620](https://github.com/foodcoopshop/foodcoopshop/pull/620) +- Auf den Produkt-Bestelllisten scheint nun auch die Summe der Produkt-Einheit (kg, g) auf. [I#333](https://github.com/foodcoopshop/foodcoopshop/issues/333) / [PR#578](https://github.com/foodcoopshop/foodcoopshop/pull/578) +- Die Software ist kompatibel mit PHP 8.0. +- Das Logo kann jetzt auch in anderen Formaten verwendet werden. Neuer Standard ist PNG. [PR#637](https://github.com/foodcoopshop/foodcoopshop/pull/637) + +### Bugfixes +- Bestelllisten- und Rechnungsversand werden jetzt über eine Queue versendet. Das verhindert seltene, aber nervige Fehler beim Versenden. [I#410](https://github.com/foodcoopshop/foodcoopshop/issues/410) / [I#560](https://github.com/foodcoopshop/foodcoopshop/issues/560) / [I#562](https://github.com/foodcoopshop/foodcoopshop/issues/562) / [PR#561](https://github.com/foodcoopshop/foodcoopshop/pull/561) / [PR#566](https://github.com/foodcoopshop/foodcoopshop/pull/566) / [PR#553](https://github.com/foodcoopshop/foodcoopshop/pull/553) +- Hochgeladene Bilder (z.B. Slideshow) waren machmal leicht unscharf. [I#558](https://github.com/foodcoopshop/foodcoopshop/issues/558) / [PR#573](https://github.com/foodcoopshop/foodcoopshop/pull/573) +- Deutsche IBANs können nun eingetragen werden, das Feld war zu kurz. [I#559](https://github.com/foodcoopshop/foodcoopshop/issues/559) / [PR#564](https://github.com/foodcoopshop/foodcoopshop/pull/564) +- Lieferrhythmus "erster Freitag im Monat" kombiniert mit "Sonntag Bestellschluss" hat nicht korrekt funktioniert. [I#567](https://github.com/foodcoopshop/foodcoopshop/issues/567) / [PR#568](https://github.com/foodcoopshop/foodcoopshop/pull/568) +- Monatlicher Lieferrhythmus kombiniert mit "erster Liefertag" hat nicht korrekt funktioniert. [I#623](https://github.com/foodcoopshop/foodcoopshop/issues/623) / [PR#624](https://github.com/foodcoopshop/foodcoopshop/pull/624) + +### Code Cleaning +- Die Übersetzung auf Polnisch wurde entfernt. Sie wurde nicht mehr verwendet und auch nicht mehr upgedatet. [I#631](https://github.com/foodcoopshop/foodcoopshop/issues/631) / [PR#632](https://github.com/foodcoopshop/foodcoopshop/pull/632) +- CI-Umstellung von Travis auf Github Actions [PR#556](https://github.com/foodcoopshop/foodcoopshop/pull/556) + +### Deprecations +- ⚠️⚠️⚠️ Wer das [Stundenabrechnungs-Modul](https://foodcoopshop.github.io/de/stundenabrechnungs-modul.html) aktiv verwendet, soll sich bitte bei mir melden. Ich habe nämlich den Eindruck, dass es kaum in Gebrauch ist. Da aber die Wartung Aufwand bedeutet, werde ich das Modul - sofern sich bis dahin niemand meldet - ab v3.3 (Herbst 2021) aus der Software entfernen. + +Datum: 08.03.2021 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/14) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.1.0...v3.2.0) + +# v3.1.0 + +### Herzlichen Dank an alle beteiligten Personen +* [AndreasEgger](https://github.com/AndreasEgger) +* [dpakach](https://github.com/dpakach) +* [mrothauer](https://github.com/mrothauer) +* [swoichha](https://github.com/swoichha) +* [vmvbruck](https://github.com/vmvbruck) + +### Neue Funktionen +- Automatischer Kontoabgleich für das Guthaben-System (CSV-Upload). [Zur Online-Doku](https://foodcoopshop.github.io/de/guthaben-system-mit-automatischem-kontoabgleich). [I#463](https://github.com/foodcoopshop/foodcoopshop/issues/463) / [PR#474](https://github.com/foodcoopshop/foodcoopshop/pull/474) +- Mitglieder können nun Feedback zu Produkten abgeben, der Hersteller wird automatisch per E-Mail darüber informiert. [I#391](https://github.com/foodcoopshop/foodcoopshop/issues/391) / [PR#536](https://github.com/foodcoopshop/foodcoopshop/pull/536) +- Viele Overlays (z.B. "Gewicht ändern", "Bild-Upload", "Abmelden") sind nun benutzerfreundlicher und systemweit vereinheitlicht. [I#328](https://github.com/foodcoopshop/foodcoopshop/issues/328) / [PR#524](https://github.com/foodcoopshop/foodcoopshop/pull/524) / [PR#530](https://github.com/foodcoopshop/foodcoopshop/pull/530) / [PR#537](https://github.com/foodcoopshop/foodcoopshop/pull/537) / [PR#538](https://github.com/foodcoopshop/foodcoopshop/pull/538) +- Verbesserungen bei der Gewichtsanpassung: Auch gleiches Gewicht ist nach dem Speichern nicht mehr rot hinterlegt. / Bei bereits verrechneten Bestellungen wird das Gewicht niemals rot angezeigt. / Neues Gewicht ist in der E-Mail-Betreffzeile - damit Fehler wie z.B. 540 kg (statt g) schneller auffallen. / Kein E-Mail-Versand falls das Gewicht gleich bleibt. [I#423](https://github.com/foodcoopshop/foodcoopshop/issues/423) / [PR#479](https://github.com/foodcoopshop/foodcoopshop/pull/479) +- Es ist jetzt möglich, als Bestellschluss für bestimmte Produkte auch **zwei Tage** vor dem Standard-Bestellschluss auszuwählen. Bisher war das nur für den Vortag möglich. [I#487](https://github.com/foodcoopshop/foodcoopshop/issues/487) / [PR#489](https://github.com/foodcoopshop/foodcoopshop/pull/489) +- 😍 Ab sofort können Emojis verwendet werden: Z.B. in Blog-Artikeln, Seiten oder beim Stornieren. Im Editor gibt's dazu ein neues Icon, beim Tippen von einem Doppelpunkt und zwei Buchstaben wird automatisch eine Liste mit Emojis angezeigt. [I#464](https://github.com/foodcoopshop/foodcoopshop/issues/464) / [PR#478](https://github.com/foodcoopshop/foodcoopshop/pull/478) +- Falls Produkte auch für uneingeloggte Mitglieder angezeigt werden, wird nun auch der In-den-Warenkorb-Button angezeigt. Wenn man darauf klickt, erhält man die Meldung, dass man sich zuerst registrieren muss. [I#499](https://github.com/foodcoopshop/foodcoopshop/issues/499) / [PR#500](https://github.com/foodcoopshop/foodcoopshop/pull/500) +- Neue Produkte werden nun auch auf der Startseite angezeigt. Das kann in den Einstellungen ausgestellt werden. [I#504](https://github.com/foodcoopshop/foodcoopshop/issues/504) / [PR#506](https://github.com/foodcoopshop/foodcoopshop/pull/506) +- Kunden von Hofläden können den Abholtag selbst beim Bestellabschluss auswählen. [Zur Online-Doku](https://foodcoopshop.github.io/de/hofladen-online.html). [PR#542](https://github.com/foodcoopshop/foodcoopshop/pull/542) + +### Bugfixes / Optimierungen +- Beim Ändern der Anzahl bzw. Anzahl-Einstellungen von Produkten wird der alte Wert nun wieder unter Aktivitäten angezeigt. [I#514](https://github.com/foodcoopshop/foodcoopshop/issues/514) / [PR#515](https://github.com/foodcoopshop/foodcoopshop/pull/515) +- Horizontales Scrollen auf kleinen Bildschirmen hat das Layout zerschossen. [I#497](https://github.com/foodcoopshop/foodcoopshop/issues/497) / [PR#498](https://github.com/foodcoopshop/foodcoopshop/pull/498) +- Man bleibt jetzt 30 Tage lang angemeldet, wenn man die Funkion "Angemeldet bleiben" verwendet. Bisher waren es 6. [I#492](https://github.com/foodcoopshop/foodcoopshop/issues/492) / [PR#493](https://github.com/foodcoopshop/foodcoopshop/pull/493) +- Die Daten für die Mitglieder-Drodowns im Admin-Bereich werden nun erst nach dem Daraufklicken geladen. Das lädt die Seiten schneller, besonders bei Initativen mit vielen Mitgliedern. [I#477](https://github.com/foodcoopshop/foodcoopshop/issues/477) / [PR#501](https://github.com/foodcoopshop/foodcoopshop/pull/501) +- Die automatische Zeichenbeschränkung in Formularen hat nicht mehr funktioniert (z.B. Feld "Kammer", Feld "Vorname") und führte zu einem Datenbank-Fehler. [I#485](https://github.com/foodcoopshop/foodcoopshop/issues/485) / [I#521](https://github.com/foodcoopshop/foodcoopshop/issues/521) / [PR#488](https://github.com/foodcoopshop/foodcoopshop/pull/525) / [PR#525](https://github.com/foodcoopshop/foodcoopshop/pull/488) +- Infotext bei der Lieferpause ist jetzt leichter verständlich. [I#469](https://github.com/foodcoopshop/foodcoopshop/issues/469) / [PR#482](https://github.com/foodcoopshop/foodcoopshop/pull/482) +- Bestelllisten sind ab und zu nicht über die Fallback-Konfiguration versendet worden. [I#495](https://github.com/foodcoopshop/foodcoopshop/issues/495) / [PR#496](https://github.com/foodcoopshop/foodcoopshop/pull/496) +- Der neue PDF-Writer kann nun PDFs unabhängig von Controllern erzeugen (als Attachment, Inline oder File). [I#412](https://github.com/foodcoopshop/foodcoopshop/issues/412) / [PR#508](https://github.com/foodcoopshop/foodcoopshop/pull/508) +- Bei der Validierung der E-Mail-Adressen wird jetzt auch der MX-Eintrag überprüft. Das vermeidet das Eintragen von ungültigen E-Mail-Adressen, die zwar syntaktisch korrekt sind, bei denen sich aber beim Domainnamen ein Tippfehler eingeschlichen hat. [I#465](https://github.com/foodcoopshop/foodcoopshop/issues/465) / [PR#516](https://github.com/foodcoopshop/foodcoopshop/pull/516) +- Home- und Blog-Slider: OwlCarousel2 wurde ersetzt mit Swiper. [I#512](https://github.com/foodcoopshop/foodcoopshop/issues/512) / [PR#535](https://github.com/foodcoopshop/foodcoopshop/pull/535) +- All tests now work without HttpClient and use IntegrationTestTrait, the tests are now about 45% faster! [I#404](https://github.com/foodcoopshop/foodcoopshop/issues/404) / [PR#550](https://github.com/foodcoopshop/foodcoopshop/pull/550) / [PR#529](https://github.com/foodcoopshop/foodcoopshop/pull/529) / [PR#531](https://github.com/foodcoopshop/foodcoopshop/pull/531) / [PR#532](https://github.com/foodcoopshop/foodcoopshop/pull/532) +- FoodCoopShop verwendet jetzt CakePHP v4.1.x. [I#541](https://github.com/foodcoopshop/foodcoopshop/issues/541) / [PR#545](https://github.com/foodcoopshop/foodcoopshop/pull/545) + +Datum: 07.09.2020 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/13) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.0.2...v3.1.0) + +# v3.0.2 + +### Bugfix +- Produkte waren fehlerhafterweise bestellbar, wenn das Produkt früher mal als Lagerprodukt deklariert war und das Feld "Bestellbar bis zu einer Anzahl von" einen Wert < 0 enthielt. +- Es gab immer wieder Probleme beim automatischen Vermindern der Anzahl, wenn im gleichen Warenkorb ein Produkt mit einer Variante vorhanden war und dieses Produkt genau vor dem entsprechenden Produkt gereiht war. War schwer zu finden... / [PR#484](https://github.com/foodcoopshop/foodcoopshop/pull/484) + +Datum: 26.03.2020 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.0.1...v3.0.2) + +# v3.0.1 + +### Bugfix +- Kategorien wurden nicht korrekt sortiert. / [C](https://github.com/foodcoopshop/foodcoopshop/commit/35d940d82912200d6aab60dd6adc5fedbb68b4de) + +Datum: 22.03.2020 / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v3.0.0...v3.0.1) + +# v3.0.0 + +### Herzlichen Dank an alle beteiligten Personen +* [AndreasEgger](https://github.com/AndreasEgger) +* [mrothauer](https://github.com/mrothauer) + +### Neue Funktionen +- Bei Produkten kann nun als Anzahl "immer verfügbar" eingestellt werden. Weiters kann mittels "Standard-Anzahl pro Lieferrhythmus" festgelegt werden, auf welche verfügbare Anzahl nach erfolgtem Bestelllisten-Versand automatisch wieder hochgezählt wird. Details in der [Online-Dokumentation](https://foodcoopshop.github.io/de/produkte). [I#452](https://github.com/foodcoopshop/foodcoopshop/issues/452) / [I#324](https://github.com/foodcoopshop/foodcoopshop/issues/324) / [PR#457](https://github.com/foodcoopshop/foodcoopshop/pull/457) / +- Das Hauptmenü des Frontends wird nun eingeblendet, sobald man nach oben scrollt. [I#438](https://github.com/foodcoopshop/foodcoopshop/issues/438) / [PR#440](https://github.com/foodcoopshop/foodcoopshop/pull/440) +- Produkte von Sammelbestellungen, bei denen die Bestellfrist bereits erreicht wurde, können über die Sofort-Bestellung jetzt trotzdem bestellt werden. Das ist praktisch für Nachbuchungen. [I#443](https://github.com/foodcoopshop/foodcoopshop/issues/454) / [PR#454](https://github.com/foodcoopshop/foodcoopshop/pull/440) + +### Bugfixes / Updates +- Die Auto-Login-Funktion ("Angemeldet bleiben") hat nicht mehr richtig funktioniert. [I#439](https://github.com/foodcoopshop/foodcoopshop/issues/439) / [PR#444](https://github.com/foodcoopshop/foodcoopshop/pull/444) +- Beim Löschen eines Mitgliedes werden die Bestellungen nun auf "mit dem Hersteller verrechnet" überprüft (und nicht mehr, ob sie 2 Monate alt sind). Weiters wird überprüft, ob die Guthaben-Aufladungen der letzten zwei Jahre bestätigt sind. [I#451](https://github.com/foodcoopshop/foodcoopshop/issues/451) / [PR#456](https://github.com/foodcoopshop/foodcoopshop/pull/456) +- Unter "Aktivitäten" wird ab sofort bei Einträgen des Rechnungsversands die korrekte Uhrzeit angezeigt. Diese war bisher auf 00:00 gesetzt. [I#451](https://github.com/foodcoopshop/foodcoopshop/issues/451) / [PR#455](https://github.com/foodcoopshop/foodcoopshop/pull/455) +- FoodCoopShop verwendet jetzt [CakePHP v4.0](https://book.cakephp.org/4/en/index.html). [I#445](https://github.com/foodcoopshop/foodcoopshop/issues/445) / [PR#446](https://github.com/foodcoopshop/foodcoopshop/pull/446) +- FoodCoopShop ist jetzt mit PHP 7.4 kompatibel. [I#448](https://github.com/foodcoopshop/foodcoopshop/issues/448) / [PR#449](https://github.com/foodcoopshop/foodcoopshop/pull/449) +- Die Begrenzung der maximalen Zeichenanzahl bei Seiten, Blog-Artikel, Produkt- und Herstellerbeschreibung wurde erhöht. [I#460](https://github.com/foodcoopshop/foodcoopshop/issues/460) / [PR#462](https://github.com/foodcoopshop/foodcoopshop/pull/462) +- Neue Kategorien waren nicht mehr alfabetisch sortiert. [I#458](https://github.com/foodcoopshop/foodcoopshop/issues/458) / [PR#459](https://github.com/foodcoopshop/foodcoopshop/pull/459) +- Legacy-Code von FCS v2 wurde entfernt. [PR#468](https://github.com/foodcoopshop/foodcoopshop/pull/468) + +Datum: 20.03.2020 / [Mehr Details zum Release](https://github.com/foodcoopshop/foodcoopshop/projects/12) / [Alle Änderungen anzeigen](https://github.com/foodcoopshop/foodcoopshop/compare/v2.7.1...3.0.0) + +[Zum Changelog von FoodCoopShop v2.x](devtools/CHANGELOG-v2.md) diff --git a/devtools/composer-post-install.sh b/devtools/composer-post-install.sh index 774902edfa..eb8d616250 100644 --- a/devtools/composer-post-install.sh +++ b/devtools/composer-post-install.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -bash devtools/setup-git.sh - # remove overhead from tcpdf library rm -Rf vendor/tecnickcom/tcpdf/examples diff --git a/devtools/db_dump_all b/devtools/db_dump_all deleted file mode 100644 index b80a18dedb..0000000000 --- a/devtools/db_dump_all +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -# get the script dir -DIR=`php -r "echo dirname(realpath('$0'));"` - -###################################### -# -# HOW TO CREATE NEW DATABASE DUMPS (if a new migration was created) -# -# 1) rename database in default db config to "foodcoopshop_tmp" or whatever name -# 3) run devtools/db_dump_all - triggers error on first call as "foodcoopshop_tmp" does not exist yet. just ignore. -# 4) rename database in default db config to initial name -# 5) commit the modified database dumps -# -###################################### - - -source $(dirname $0)/locales.sh -for locale in "${LOCALES[@]}" -do - php -f $DIR/db_dump_install.php $locale -done - -php -f $DIR/db_dump_test.php de_DE diff --git a/devtools/db_dump_common.php b/devtools/db_dump_common.php deleted file mode 100755 index 16315b5195..0000000000 --- a/devtools/db_dump_common.php +++ /dev/null @@ -1,232 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -if (!defined('DATASOURCE')) { - exit('Do not use directly.'); -} - -$tmpDbName = 'foodcoopshop_tmp'; - -$datasource = array( - 'PROD' => array( - 'structure' => 'config' . DS . 'sql' . DS . '_installation' . DS . 'clean-db-structure.sql', - 'data' => 'config' . DS . 'sql' . DS . '_installation' . DS . 'clean-db-data-' . $locale . '.sql', - ), - 'TEST' => array( - 'structure' => 'config' . DS . 'sql' . DS . '_installation' . DS . 'clean-db-structure.sql', - 'data' => 'tests' . DS . 'config' . DS . 'sql' . DS . 'test-db-data.sql', - ), -); - -echo 'Loading config for ' . DATASOURCE . ', locale: ' . $locale . ' '; - -// get the project dir from being in [project]/devtools/ -$dir = dirname(realpath(__DIR__)) . DS; - -// include DB config -$config = @include $dir . 'config' . DS . 'custom_config.php'; -$dbConfig = $config['Datasources']; - -if (DATASOURCE == 'PROD') { - if (empty($dbConfig['default'])) { - exit(PHP_EOL . 'Cannot read config' . DS . 'custom_config.php.' . PHP_EOL); - } - $db_conf = $dbConfig['default']; // use production DB -} - -if (DATASOURCE == 'TEST') { - if (empty($dbConfig['test'])) { - exit(PHP_EOL . 'Cannot read config' . DS . 'custom_config.php.' . PHP_EOL); - } - $db_conf = $dbConfig['test']; // use test DB -} - -if (empty($db_conf)) { - exit(PHP_EOL . 'Unknown datasource.' . PHP_EOL); -} - -echo 'done' . PHP_EOL; -echo 'Reading dump command...'; - -$unmodifiedStructureFile = $dir . 'devtools' . DS . 'unmodified-structure.sql'; -if (DATASOURCE == 'PROD' && $locale == 'de_DE') { - copy($dir . $datasource[DATASOURCE]['structure'], $unmodifiedStructureFile); -} -$mysqldump_cmd = ''; -$mysql_cmd = ''; -$lines = @file($dir . 'config' . DS . 'app_config.php'); -require('get_mysqldump_cmd.php'); -require('get_mysql_cmd.php'); - -if (file_exists($dir . 'config' . DS . 'custom_config.php')) { - $lines = @file($dir . 'config' . DS . 'custom_config.php'); - require('get_mysqldump_cmd.php'); - require('get_mysql_cmd.php'); -} - -echo 'done' . PHP_EOL; -echo 'Resetting database and executing migrations...'; -$cmd = sprintf('"%1$s" -h %2$s -u %3$s -p%4$s -e "DROP DATABASE %5$s;"', $mysql_cmd, $db_conf['host'], $db_conf['username'], $db_conf['password'], $tmpDbName); -exec($cmd); -$cmd = sprintf('"%1$s" -h %2$s -u %3$s -p%4$s -e "CREATE DATABASE %5$s;"', $mysql_cmd, $db_conf['host'], $db_conf['username'], $db_conf['password'], $tmpDbName); -exec($cmd); -$cmd = sprintf('"%1$s" -h %2$s -u %3$s -p%4$s %5$s < %6$s', $mysql_cmd, $db_conf['host'], $db_conf['username'], $db_conf['password'], $tmpDbName, $unmodifiedStructureFile); -exec($cmd); -$cmd = sprintf('"%1$s" -h %2$s -u %3$s -p%4$s %5$s < %6$s', $mysql_cmd, $db_conf['host'], $db_conf['username'], $db_conf['password'], $tmpDbName, $dir . $datasource[DATASOURCE]['data']); -exec($cmd); - -$cmd = 'bash ' . $dir . 'bin/cake migrations migrate'; -exec($cmd, $result); -foreach ($result as $line) { - echo PHP_EOL . $line; -} - -$cmd = 'bash ' . $dir . 'bin/cake migrations migrate -p Queue'; -exec($cmd, $result); -foreach ($result as $line) { - echo PHP_EOL . $line; -} - -echo PHP_EOL . 'Dumping structure...'; -$result = array(); -$cmd = sprintf('"%1$s" --host="%2$s" --user="%3$s" --password="%4$s" --no-create-db --no-data --events --routines --skip-opt --create-options --add-drop-table --disable-keys --extended-insert --quick --set-charset --quote-names --skip-comments --skip-add-locks --single-transaction --force --result-file="%5$s" %6$s 2>&1', $mysqldump_cmd, $db_conf['host'], $db_conf['username'], $db_conf['password'], $dir . $datasource[DATASOURCE]['structure'] . '.tmp', $tmpDbName); -exec($cmd, $result); - -foreach ($result as $line) { - echo PHP_EOL . $line; -} -if (!empty($result)) { - echo PHP_EOL; -} - -// check dump has a result file with some content of > 4k bytes (no error message should be that long) -clearstatcache(); -if (!is_readable($datasource[DATASOURCE]['structure'] . '.tmp') - || filesize($datasource[DATASOURCE]['structure'] . '.tmp') < 4096 -) { - exit(PHP_EOL . 'Structure not dumped. Seek for help!' . PHP_EOL); -} - -rename($datasource[DATASOURCE]['structure'] . '.tmp', $datasource[DATASOURCE]['structure']); - -echo 'done' . PHP_EOL; -echo 'Dumping data...'; - -$result = array(); -$cmd = sprintf('"%1$s" --host="%2$s" --user="%3$s" --password="%4$s" --no-create-info --skip-opt --create-options --disable-keys --extended-insert --quick --set-charset --quote-names --skip-comments --skip-add-locks --single-transaction --force --result-file="%5$s" %6$s 2>&1', $mysqldump_cmd, $db_conf['host'], $db_conf['username'], $db_conf['password'], $dir . $datasource[DATASOURCE]['data'] . '.tmp', $tmpDbName); -exec($cmd, $result); - -foreach ($result as $line) { - echo PHP_EOL . $line; -} -if (!empty($result)) { - echo PHP_EOL; -} - -// check dump has a result file with some content of > 4k bytes (no error message should be that long) -clearstatcache(); -if (!is_readable($datasource[DATASOURCE]['data'] . '.tmp') - || filesize($datasource[DATASOURCE]['data'] . '.tmp') < 4096 -) { - exit(PHP_EOL . 'Data not dumped. Seek for help!' . PHP_EOL); -} - -rename($datasource[DATASOURCE]['data'] . '.tmp', $datasource[DATASOURCE]['data']); - -echo 'done' . PHP_EOL; -echo 'Strip autoincrement value from structure dump...'; - -$infile = fopen($dir . $datasource[DATASOURCE]['structure'], 'rb'); -$outfile = fopen($dir . $datasource[DATASOURCE]['structure'] . '.tmp', 'wb'); -if ($infile === false) { - exit(PHP_EOL . 'Cannot open ' . $datasource[DATASOURCE]['structure'] . 'for reading' . PHP_EOL); -} -if ($outfile === false) { - exit(PHP_EOL . 'Cannot open ' . $datasource[DATASOURCE]['structure'] . '.tmp' . 'for writing' . PHP_EOL); -} - -while (!feof($infile)) { - $line = fgets($infile); - if (stripos($line, 'AUTO_INCREMENT=') !== false) { - $line = preg_replace('/AUTO_INCREMENT=[0-9]*/i', '', $line); - } - fwrite($outfile, $line); -} -fclose($infile); -fclose($outfile); - -rename($datasource[DATASOURCE]['structure'] . '.tmp', $datasource[DATASOURCE]['structure']); - -echo 'done' . PHP_EOL; -echo 'Insert line breaks into inserts in data dump...'; - -$infile = fopen($dir . $datasource[DATASOURCE]['data'], 'rb'); -$outfile = fopen($dir . $datasource[DATASOURCE]['data'] . '.tmp', 'wb'); -if ($infile === false) { - exit(PHP_EOL . 'Cannot open ' . $datasource[DATASOURCE]['data'] . 'for reading' . PHP_EOL); -} -if ($outfile === false) { - exit(PHP_EOL . 'Cannot open ' . $datasource[DATASOURCE]['data'] . '.tmp' . 'for writing' . PHP_EOL); -} - -while (!feof($infile)) { - $line = fgets($infile); - $line = str_replace('VALUES (', 'VALUES' . PHP_EOL . '(', $line); - $line = str_replace('),(', '),' . PHP_EOL . '(', $line); - fwrite($outfile, $line); -} -fclose($infile); -fclose($outfile); - -rename($datasource[DATASOURCE]['data'] . '.tmp', $datasource[DATASOURCE]['data']); - -echo 'done' . PHP_EOL; -echo 'Add table truncation to data dump...'; - -// get the table names from structure dump -$truncates = array(); -exec('grep -i -e "^CREATE TABLE" "' . $dir . $datasource[DATASOURCE]['structure'] . '"', $truncates); -foreach ($truncates as $k => $v) { - $truncates[$k] = str_ireplace(array('CREATE TABLE', ' ('), array('TRUNCATE TABLE', ';'), $v); -} - -$infile = fopen($dir . $datasource[DATASOURCE]['data'], 'rb'); -$outfile = fopen($dir . $datasource[DATASOURCE]['data'] . '.tmp', 'wb'); -if ($infile === false) { - exit(PHP_EOL . 'Cannot open ' . $datasource[DATASOURCE]['data'] . 'for reading' . PHP_EOL); -} -if ($outfile === false) { - exit(PHP_EOL . 'Cannot open ' . $datasource[DATASOURCE]['data'] . '.tmp' . 'for writing' . PHP_EOL); -} - -$done = false; -while (!feof($infile)) { - $line = fgets($infile); - if (!$done && (stripos($line, '/*!40000 ALTER TABLE') === 0)) { - $done = true; - fwrite($outfile, '-- Truncate tables before insertion' . PHP_EOL); - foreach ($truncates as $v) { - fwrite($outfile, $v . PHP_EOL); - } - fwrite($outfile, PHP_EOL); - } - fwrite($outfile, $line); -} -fclose($infile); -fclose($outfile); - -rename($datasource[DATASOURCE]['data'] . '.tmp', $datasource[DATASOURCE]['data']); - -echo 'done' . PHP_EOL; diff --git a/devtools/db_dump_install.php b/devtools/db_dump_install.php deleted file mode 100755 index 941f80a9bf..0000000000 --- a/devtools/db_dump_install.php +++ /dev/null @@ -1,25 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -if (!defined('DATASOURCE')) { - define('DATASOURCE', 'PROD'); -} - -if (!defined('DS')) { - define('DS', DIRECTORY_SEPARATOR); -} - -$locale= $argv[1]; -include realpath(__DIR__) . DS . 'db_dump_common.php'; diff --git a/devtools/get_mysql_cmd.php b/devtools/get_mysql_cmd.php deleted file mode 100644 index c26cc54cef..0000000000 --- a/devtools/get_mysql_cmd.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -if (!is_array($lines) - || empty($lines) - ) { - exit(PHP_EOL . 'Cannot load config' . DS . 'app_config.php.' . PHP_EOL); - } - - foreach ($lines as $line) { - if (($pos = strpos($line, '\'mysqlCommand\'')) !== false) { - $line = substr($line, $pos + strlen('\'mysqlCommand\',')); - $line = explode('\'', $line); - if (count($line) == 3) { - $mysql_cmd = $line[1]; - } else { - $mysql_cmd = $line[0]; - } - } - } - - if (empty($mysql_cmd)) { - exit(PHP_EOL . 'Cannot read mysqlCommand from Config' . DS . 'app_config.php.' . PHP_EOL); - } - - if (strpos($mysql_cmd, 'mysql') === false) { - exit(PHP_EOL . 'Cannot use mysqlCommand from Config' . DS . 'app_config.php. Must use mysql' . PHP_EOL); - } -?> \ No newline at end of file diff --git a/devtools/get_mysqldump_cmd.php b/devtools/get_mysqldump_cmd.php deleted file mode 100644 index d7b857fe7d..0000000000 --- a/devtools/get_mysqldump_cmd.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -if (!is_array($lines) - || empty($lines) - ) { - exit(PHP_EOL . 'Cannot load config' . DS . 'app_config.php.' . PHP_EOL); - } - - foreach ($lines as $line) { - if (($pos = strpos($line, '\'mysqlDumpCommand\'')) !== false) { - $line = substr($line, $pos + strlen('\'mysqlDumpCommand\',')); - $line = explode('\'', $line); - if (count($line) == 3) { - $mysqldump_cmd = $line[1]; - } else { - $mysqldump_cmd = $line[0]; - } - } - } - - if (empty($mysqldump_cmd)) { - exit(PHP_EOL . 'Cannot read mysqlDumpCommand from Config' . DS . 'app_config.php.' . PHP_EOL); - } - - if (strpos($mysqldump_cmd, 'mysqldump') === false) { - exit(PHP_EOL . 'Cannot use mysqlDumpCommand from Config' . DS . 'app_config.php. Must use mysqldump' . PHP_EOL); - } -?> \ No newline at end of file diff --git a/devtools/git-pre-commit.sh b/devtools/git-pre-commit.sh deleted file mode 100755 index 65c594f8b6..0000000000 --- a/devtools/git-pre-commit.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bash -# -# - git checkout the staged versions to temp dir -# - run PHP lint on temp dir -# -- checking *.php, *.inc and *.ctp -# -- see fcs-ruleset.xml, too -# -- abort commit on errors -# - run phpcbf on the temp dir () -# -- applying automatic repairs for tests in fcs-rules.xml -# -- don't abort commit -# - run phpcs on the temp dir -# -- must pass all tests in fcs-rules.xml now -# -- abort on errors -# - re-add all changes -# - remove temp dir -# - allow commit - -# CodeSniffer and CodeBeautifierFixer commands -PHPCBF="vendor/bin/phpcbf --standard=devtools/fcs-rules.xml" -PHPCS="vendor/bin/phpcs --standard=devtools/fcs-rules.xml" - -# note the current directory -PWD=`pwd` - -# get the project dir from being in [project]/devtools/ and change to it -DIR=`php -r "echo dirname(dirname(realpath('$0')));"` -cd "$DIR" - -STAGED_FILES=`git diff --cached --name-only --diff-filter=ACMR HEAD` -if [ "x$STAGED_FILES" == "x" ] -then - echo "Nothing to do on pre-commit" - exit 0 -fi - -# the temp directory used, within $DIR -WORK_DIR=`mktemp -d --tmpdir="$DIR" git.XXXXXXXX` - -# check if tmp dir was created -if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then - echo "User `whoami` could not create temp dir in $DIR" - exit 1 -fi -echo "Created temp dir $WORK_DIR" - -# deletes the temp directory -function cleanup { - rm -rf "$WORK_DIR" - echo "Deleted temp working directory $WORK_DIR" - cd "$PWD" -} - -# register the cleanup function to be called on the EXIT signal -trap cleanup EXIT - -# get a temp copy of all staged files -while read -r file <&3 || [[ -n "$file" ]]; -do - if [ "$file" != "" ] - then - `git checkout-index --prefix="$WORK_DIR/" -- "$file"` - phpfile=`echo $file | grep \\\\.php` - phpfile2=`echo $file | grep \\\\.ctp` - phpfile="$phpfile$phpfile2" - phpfile2=`echo $file | grep \\\\.inc` - phpfile="$phpfile$phpfile2" - if [ "$phpfile" != "" ] - then - php -l -d display_errors=0 "$WORK_DIR/$file" - if [ $? != 0 ] - then - echo "Fix the error(s) before commit." - exit 1 - fi - fi - fi -done 3<<< "$STAGED_FILES" - -# do the coding standards tests -$PHPCBF "$WORK_DIR" - -$PHPCS "$WORK_DIR" -if [ $? != 0 ] -then - # there were problems that could not be solved - echo "Fix the error(s) and add the changed files before next commit." - exit 1 -fi - -# add the changed files to staging area -while read -r file <&3 || [[ -n "$file" ]]; -do - if [ "$file" != "" ] - then - # do not overwrite worktree files - if [ -f "$DIR/$file" ] - then - cp -f "$DIR/$file" "$DIR/$file.save" 2> /dev/null - fi - - cp -f "$WORK_DIR/$file" "$DIR/$file" 2> /dev/null - `git add "$file"` - - if [ -f "$DIR/$file.save" ] - then - mv -f "$DIR/$file.save" "$DIR/$file" 2> /dev/null - fi - fi -done 3<<< "$STAGED_FILES" - -exit 0 diff --git a/devtools/init-dev-setup.sh b/devtools/init-dev-setup.sh new file mode 100644 index 0000000000..9f6998459b --- /dev/null +++ b/devtools/init-dev-setup.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +bash ./devtools/installation/set-permissions.sh +bash ./devtools/setup-dev/copy-config-files.sh + +CURRENT_UID=$(id -u):$(id -g) docker exec -w /app fcs.php composer install + +CURRENT_UID=$(id -u):$(id -g) docker exec -w /app fcs.php bash ./bin/cake migrations migrate -p Queue +CURRENT_UID=$(id -u):$(id -g) docker exec -w /app fcs.php bash ./bin/cake migrations migrate +CURRENT_UID=$(id -u):$(id -g) docker exec -w /app fcs.php bash ./bin/cake migrations seed --source Seeds/tests --seed InitTestDataSeed + +CURRENT_UID=$(id -u):$(id -g) docker exec -w /app/webroot fcs.php npm install \ No newline at end of file diff --git a/devtools/installation/init-database.sh b/devtools/installation/init-database.sh new file mode 100644 index 0000000000..05c03cc025 --- /dev/null +++ b/devtools/installation/init-database.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +locale=$1 + +if [[ "$locale" == "" ]]; then + source ./devtools/locales.sh + localeConcat=''; + for locale in "${LOCALES[@]}" + do + localeConcat+="$locale " + done + echo "locale is not set, allowed values: $localeConcat" + exit +fi + +bash ./bin/cake migrations migrate -p Queue +bash ./bin/cake migrations migrate +bash ./bin/cake migrations seed --source Seeds/locale/$locale --seed InitDataSeed +bash ./bin/cake migrations seed --seed InitDataSeed diff --git a/devtools/installation/set-permissions.sh b/devtools/installation/set-permissions.sh new file mode 100644 index 0000000000..e752c4b0b5 --- /dev/null +++ b/devtools/installation/set-permissions.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +chmod -R a+w ./logs +chmod -R a+w ./tmp +chmod -R a+w ./webroot/tmp +chmod -R a+w ./webroot/files +chmod -R a+w ./webroot/cache +chmod -R a+w ./files_private diff --git a/devtools/npm-post-install.sh b/devtools/npm-post-install.sh new file mode 100644 index 0000000000..531cb806a3 --- /dev/null +++ b/devtools/npm-post-install.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# allows script to be called from /webroot and root directory +SCRIPT=$(readlink -f "$0") +APP=$(dirname "$SCRIPT")/.. + +rm -Rf $APP/webroot/node_modules/jquery-backstretch/examples +rm -Rf $APP/webroot/node_modules/jquery-backstretch/test +rm -Rf $APP/webroot/node_modules/@fortawesome/fontawesome-free/js +rm -Rf $APP/webroot/node_modules/@fortawesome/fontawesome-free/metadata +rm -Rf $APP/webroot/node_modules/@fortawesome/fontawesome-free/svgs +rm $APP/webroot/node_modules/@fortawesome/fontawesome-free/css/all.min.css 2> /dev/null +rm $APP/webroot/node_modules/@fortawesome/fontawesome-free/css/fontawesome.css 2> /dev/null +rm $APP/webroot/node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css 2> /dev/null +rm $APP/webroot/node_modules/@fortawesome/fontawesome-free/css/v4-shims.css 2> /dev/null +rm $APP/webroot/node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css 2> /dev/null +rm -Rf $APP/webroot/node_modules/jquery-ui/external +rm -Rf $APP/webroot/node_modules/tooltipster/demo +rm -Rf $APP/webroot/node_modules/tooltipster/doc +rm -Rf $APP/webroot/node_modules/chart.js/dist/docs + +cp -R $APP/webroot/node_modules/@fortawesome/fontawesome-free/webfonts $APP/webroot +cp -R $APP/webroot/node_modules/jquery-ui/dist/themes/smoothness/images $APP/webroot/cache + +cp $APP/config/elfinder/elfinder.html $APP/webroot/js/elfinder/elfinder.html +cp $APP/config/elfinder/php/connector.minimal.php $APP/webroot/js/elfinder/php/connector.minimal.php diff --git a/devtools/pre-commit b/devtools/pre-commit deleted file mode 100755 index 67c8552396..0000000000 --- a/devtools/pre-commit +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -# the version controlled git hook script -SCRIPT="devtools/git-pre-commit.sh" - -# get the project dir from being in [project]/.git/hooks/ and change to it -DIR=`php -r "echo dirname(dirname(dirname(realpath('$0'))));"` - -RESULT=1 -if [ -f "$DIR/$SCRIPT" ] ; then - exec "$DIR/$SCRIPT" - RESULT=$? -fi - -exit $RESULT diff --git a/devtools/setup-dev/copy-config-files.sh b/devtools/setup-dev/copy-config-files.sh new file mode 100644 index 0000000000..039ce936f2 --- /dev/null +++ b/devtools/setup-dev/copy-config-files.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +cp -n ./config/custom_config.dev.php ./config/custom_config.php +cp -n ./config/credentials.default.php ./config/credentials.php \ No newline at end of file diff --git a/devtools/setup-git.sh b/devtools/setup-git.sh deleted file mode 100755 index f98e9379c2..0000000000 --- a/devtools/setup-git.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p .git/hooks && cp -f devtools/pre-commit .git/hooks/pre-commit -chmod +x .git/hooks/pre-commit diff --git a/devtools/update-translations.sh b/devtools/update-translations.sh index 25f3a9f464..3b5baca0ec 100644 --- a/devtools/update-translations.sh +++ b/devtools/update-translations.sh @@ -5,7 +5,7 @@ source $(dirname $0)/locales.sh #get and merge translations for main app #to extract core strings change --extract-core to "yes" -bin/cake i18n extract --output resources\\locales --paths config,src,templates --overwrite --extract-core no --merge no --no-location --exclude plugins +bash bin/cake i18n extract --output resources/locales --paths config,src,templates --overwrite --extract-core no --merge no --no-location --exclude plugins for locale in "${LOCALES[@]}" do msgmerge resources/locales/$locale/cake.po resources/locales/cake.pot --output-file=resources/locales/$locale/cake.po --width=1000 @@ -13,14 +13,14 @@ do done #get and merge translations for admin plugin -bin/cake i18n extract --plugin Admin --overwrite --extract-core no --merge yes --no-location +bash bin/cake i18n extract --plugin Admin --overwrite --extract-core no --merge yes --no-location for locale in "${LOCALES[@]}" do msgmerge plugins/Admin/resources/locales/$locale/admin.po plugins/Admin/resources/locales/default.pot --output-file=plugins/Admin/resources/locales/$locale/admin.po --width=1000 done #get and merge translations for network plugin -bin/cake i18n extract --plugin Network --overwrite --extract-core no --merge yes --no-location +bash bin/cake i18n extract --plugin Network --overwrite --extract-core no --merge yes --no-location for locale in "${LOCALES[@]}" do msgmerge plugins/Network/resources/locales/$locale/network.po plugins/Network/resources/locales/default.pot --output-file=plugins/Network/resources/locales/$locale/network.po --width=1000 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..a39ebe4e9b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,101 @@ +#The provided docker should NOT BE USED IN PRODUCTION ENVIRONMENTS! + +version: '3' + +services: + + php-nginx: + build: . + container_name: fcs.php + restart: always + user: ${CURRENT_UID} + environment: + WEB_DOCUMENT_ROOT: /app/webroot + PHP_UPLOAD_MAX_FILESIZE: 200M + PHP_POST_MAX_SIZE: 200M + volumes: + - ./:/app + ports: + - "8001:80" + depends_on: + - database-dev + - database-test + networks: + default: + aliases: + # add "127.0.0.1 foodcoopshop.test" to your hosts file to use http://foodcoopshop.test:8001 + - foodcoopshop.test + fcs: + + database-dev: + image: mysql:8.0 + container_name: fcs.database.dev + restart: always + ports: + - '3320:3306' + environment: + MYSQL_USER: user + MYSQL_PASSWORD: secret + MYSQL_DATABASE: foodcoopshop-dev + MYSQL_ALLOW_EMPTY_PASSWORD: true + MYSQL_TCP_PORT: 3320 + volumes: + - db-dev-data:/var/lib/mysql + networks: + - fcs + + database-test: + image: mysql:8.0 + container_name: fcs.database.test + restart: always + tmpfs: /var/lib/mysql:exec,size=1G #smaller size (eg. 256M) lead to failing tests + ports: + - '3321:3306' + environment: + MYSQL_USER: user + MYSQL_PASSWORD: secret + MYSQL_DATABASE: foodcoopshop-test + MYSQL_ALLOW_EMPTY_PASSWORD: true + MYSQL_TCP_PORT: 3321 + networks: + - fcs + + phpmyadmin-dev: + depends_on: + - database-dev + image: phpmyadmin/phpmyadmin + container_name: fcs.phpmyadmin.dev + restart: always + ports: + - '8080:80' + environment: + PMA_HOST: database-dev + PMA_USER: root + PMA_PORT: 3320 + UPLOAD_LIMIT: 300M + networks: + - fcs + + phpmyadmin-test: + depends_on: + - database-test + image: phpmyadmin/phpmyadmin + container_name: fcs.phpmyadmin.test + restart: always + ports: + - '8081:80' + environment: + PMA_HOST: database-test + PMA_USER: root + PMA_PORT: 3321 + UPLOAD_LIMIT: 300M + networks: + - fcs + +volumes: + db-dev-data: + +networks: + fcs: + name: fcs + driver: bridge diff --git a/phpstan.neon b/phpstan.neon index ce1c64faa3..36ddffafb6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,7 +4,13 @@ parameters: - php paths: - src + - templates + - tests - plugins/Admin/src + - plugins/Admin/templates + - plugins/Admin/tests - plugins/Network/src - bootstrapFiles: - - tests/bootstrap.php + - plugins/Network/templates + - plugins/Network/tests +rules: + - Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule diff --git a/plugins/Admin/config/routes.php b/plugins/Admin/config/routes.php index 0ce09a1945..5eb9cb7b32 100644 --- a/plugins/Admin/config/routes.php +++ b/plugins/Admin/config/routes.php @@ -1,4 +1,6 @@ \n" "Language: de_DE\n" @@ -13,7 +13,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.1.1\n" +"X-Generator: Poedit 3.2.2\n" msgid "Activities" msgstr "Aktivitäten" @@ -84,6 +84,15 @@ msgstr "Test E-Mail versenden" msgid "Test_email" msgstr "Test E-Mail" +msgid "Cronjobs" +msgstr "Cronjobs" + +msgid "Edit_cronjob" +msgstr "Cronjob bearbeiten" + +msgid "The_cronjob_{0}_has_been_changed." +msgstr "Der Cronjob {0} wurde geändert." + msgid "all_members" msgstr "alle Mitglieder" @@ -252,6 +261,15 @@ msgstr "Bestellliste" msgid "member" msgstr "Mitglied" +msgid "Order_list_with_clear_names" +msgstr "Bestellliste mit Mitgliedernamen" + +msgid "Anonymized_order_list" +msgstr "Anonymisierte Bestellliste" + +msgid "Show_order_list" +msgstr "Bestellliste anzeigen" + msgid "Order_lists" msgstr "Bestelllisten" @@ -639,6 +657,12 @@ msgstr "Das Produkt {0} vom Hersteller {1} wird nicht mehr als \"neu\" angezeigt msgid "The_product_{0}_from_manufacturer_{1}_is_shown_as_new_from_now_on_for_the_next_{2}_days." msgstr "Das Produkt {0} vom Hersteller {1} wird jetzt {2} Tage lang als \"neu\" angezeigt." +msgid "{0,plural,=1{1_product_was} other{#_products_were}}_deactivated." +msgstr "{0,plural,=1{Ein Produkt wurde} other{# Produkte wurden}} deaktiviert." + +msgid "{0,plural,=1{1_product_was} other{#_products_were}}_activated." +msgstr "{0,plural,=1{Ein Produkt wurde} other{# Produkte wurden}} aktiviert." + msgid "The_product_{0}_from_manufacturer_{1}_was_deactivated." msgstr "Das Produkt {0} vom Hersteller {1} wurde deaktiviert." @@ -681,6 +705,12 @@ msgstr "Umsatz- und Gewinnstatistik" msgid "Turnover_statistics" msgstr "Umsatzstatistik" +msgid "Total" +msgstr "Gesamt" + +msgid "Last_{0}_months" +msgstr "Letzte {0} Monate" + msgid "December" msgstr "Dezember" @@ -877,7 +907,7 @@ msgid "Description" msgstr "Beschreibung" msgid "docs_route_settings" -msgstr "einstellungen" +msgstr "settings" msgid "The_following_settings_can_be_changed_in_the_admin_area." msgstr "Die folgenden Einstellungen können im Admin-Bereich geändert werden." @@ -915,12 +945,6 @@ msgstr "Version FoodCoopShop" msgid "Last_executed_migration" msgstr "Zuletzt ausgeführte Migration" -msgid "Software_update_version" -msgstr "Software-Update / Version" - -msgid "Please_find_more_information_in_the_changelog_{0}." -msgstr "Mehr Informationen zu den Änderungen findest du im {0}." - msgid "activated" msgstr "aktiviert" @@ -942,6 +966,33 @@ msgstr "Standard-Bild für Hersteller, Breite:" msgid "Default_image_for_blog_post,_width:" msgstr "Standard-Bild für Blog-Artikel, Breite: " +msgid "Website_administration" +msgstr "Homepage-Verwaltung" + +msgid "Configurations" +msgstr "Einstellungen" + +msgid "Time_interval" +msgstr "Intervall" + +msgid "Day_of_month" +msgstr "Tag (Monat)" + +msgid "Please_select..." +msgstr "Bitte auswählen..." + +msgid "Weekday" +msgstr "Wochentag" + +msgid "Not_before_time" +msgstr "Uhrzeit" + +msgid "Cronjob_is_called_up_to_10_min_after_the_given_time." +msgstr "Der tatsächliche Aufruf kann bis zu 10 min nach dieser Uhrzeit erfolgen." + +msgid "Last_run" +msgstr "Zuletzt ausgeführt" + msgid "Old_password" msgstr "Altes Passwort" @@ -954,9 +1005,6 @@ msgstr "mindestens 8 Zeichen" msgid "New_password_again" msgstr "Neues Passwort (nochmal)" -msgid "Website_administration" -msgstr "Homepage-Verwaltung" - msgid "Financial_reports" msgstr "Finanzberichte" @@ -1089,12 +1137,6 @@ msgstr "Gruppe" msgid "Status" msgstr "Status" -msgid "Ordered_products" -msgstr "Bestellte Produkte" - -msgid "Reminder" -msgstr "Erinnerung" - msgid "Feedback" msgstr "Feedback" @@ -1335,12 +1377,6 @@ msgstr "Bestellliste nach Produkt" msgid "Order_list_by_member" msgstr "Bestellliste nach Mitglied" -msgid "Show_list_(grouped_by_product)" -msgstr "Liste anzeigen (gruppiert nach Produkt)" - -msgid "Show_list_(grouped_by_member)" -msgstr "Liste anzeigen (gruppiert nach Mitglied)" - msgid "docs_route_manufacturers" msgstr "hersteller" @@ -1467,9 +1503,6 @@ msgstr "Lieferpause" msgid "How_do_I_use_the_function_delivery_break?" msgstr "Wie verwende ich die Funktion \"Lieferpause\"?" -msgid "Please_select..." -msgstr "Bitte auswählen..." - msgid "Manufacturer_profile_and_products_are_only_visible_for_signed_in_members." msgstr "Hersteller-Profil und Produkte werden nur für eingeloggte Mitglieder angezeigt." @@ -1578,6 +1611,12 @@ msgstr "Sollen die Bestelllisten auch Lagerprodukte enthalten?" msgid "Chose_member" msgstr "Mitglied auswählen..." +msgid "Anonymize_customers?" +msgstr "Mitglieder-Daten anonymisieren?" + +msgid "Customer_data_(firstname_lastname_email)_will_either_be_hidden_or_anonymized_for_this_manufacturer." +msgstr "Personenbezogene Mitglieder-Daten (Vorname, Nachname, E-Mail, usw.) werden für diesen Hersteller systemweit anonymisiert." + msgid "Pickup_days" msgstr "Abholtage" @@ -1602,6 +1641,15 @@ msgstr "Pfandkonto anzeigen" msgid "Edit_manufacturer_settings" msgstr "Hersteller-Einstellungen bearbeiten" +msgid "grouped_by_product" +msgstr "gruppiert nach Produkt" + +msgid "Anonymized_list" +msgstr "Anonymisierte Liste" + +msgid "grouped_by_customer" +msgstr "gruppiert nach Mitglied" + msgid "Test_order_list" msgstr "Bestellliste prüfen" @@ -1809,6 +1857,9 @@ msgstr "Neu?" msgid "Delivery_rhythm" msgstr "Lieferrhythmus" +msgid "Selected_products" +msgstr "Markierte Produkte" + msgid "Storage_location" msgstr "Lagerort" @@ -1848,8 +1899,8 @@ msgstr "Netto-Einkaufspreis" msgid "Gross_turnover" msgstr "Brutto-Umsatz" -msgid "Net_turnover" -msgstr "Netto-Umsatz" +msgid "Net_turnover_selling_price" +msgstr "Netto-Umsatz (VP)" msgid "total" msgstr "gesamt" @@ -1909,7 +1960,7 @@ msgid "Order_stock_products" msgstr "Lagerprodukte bestellen" msgid "Copy_selected_email_addresses" -msgstr "Ausgewählte E-Mail-Adressen kopieren" +msgstr "Markierte E-Mail-Adressen kopieren" msgid "Generate_member_cards" msgstr "Mitgliedskarten generieren" @@ -1938,9 +1989,6 @@ msgstr "IBAN" msgid "BIC" msgstr "BIC" -msgid "Configurations" -msgstr "Einstellungen" - msgid "My_products" msgstr "Meine Produkte" @@ -1957,7 +2005,7 @@ msgid "Change_pickup_day" msgstr "Abholtag ändern" msgid "Cancel_selected_products" -msgstr "Ausgewählte Produkte stornieren" +msgstr "Markierte Produkte stornieren" msgid "Copy_all_email_addresses" msgstr "Alle E-Mail-Adressen kopieren" @@ -1989,6 +2037,9 @@ msgstr "Gib dem Hersteller Feedback zum Produkt" msgid "Click_to_change_name" msgstr "Zum Ändern des Namens anklicken" +msgid "Ordered_products" +msgstr "Bestellte Produkte" + msgid "Show_all_ordered_products_from_{0}" msgstr "Alle bestellten Produkte von {0} anzeigen" @@ -2058,14 +2109,17 @@ msgstr "Vielen Dank, dass du uns belieferst!" msgid "Customer_ID" msgstr "Mitglieds-Nr." -msgid "Calculate_selling_price_with_surcharge_for_selected_products" -msgstr "Verkaufspreis mit Aufschlag für ausgewählte Produkte berechnen" +msgid "Calculate_selling_price" +msgstr "Verkaufspreis berechnen" -msgid "Delete_selected_products" -msgstr "Ausgewählte Produkte löschen" +msgid "Delete" +msgstr "Löschen" -msgid "Edit_delivery_rhythm_for_selected_products" -msgstr "Lieferrhythmus für ausgewählte Produkte bearbeiten" +msgid "Edit_delivery_rhythm" +msgstr "Lieferrhythmus ändern" + +msgid "Edit_status" +msgstr "Status ändern" msgid "Generate_product_cards" msgstr "Produktkarten generieren" @@ -2304,6 +2358,9 @@ msgstr "Deine Rechnung befindet sich im Anhang." msgid "{0}_thanks_you_for_your_purchase!" msgstr "{0} sagt Danke für Deinen Einkauf!" +msgid "Pay_this_invoice_with_paypal" +msgstr "Diese Rechnung mit Paypal bezahlen" + msgid "this_invoice_contains_all_products_that_have_been_delivered_in_{0}." msgstr "in dieser Rechnung sind alle Produkte enthalten, die im {0} geliefert wurden." @@ -2370,15 +2427,53 @@ msgstr "Die Gesamtsumme ganz rechts (Preis inkl.) wird so bald wie möglich auf msgid "Detail_view" msgstr "Detailübersicht" -msgid "Total" -msgstr "Gesamt" - msgid "The_delivered_weight_will_eventually_be_adapted_which_means_the_price_can_change_slightly." msgstr "Das tatsächlich gelieferte Gewicht wird evtl. noch angepasst, d. h. der Preis kann sich noch geringfügig ändern." msgid "Product_ID" msgstr "Produkt-Nr." +#~ msgid "Net_turnover" +#~ msgstr "Netto-Umsatz" + +#~ msgid "List_with_clear_names" +#~ msgstr "Liste mit Klarnamen" + +#, fuzzy +#~ msgid "Anonymize_members" +#~ msgstr "Mitglieder-Daten anonymisieren?" + +#~ msgid "Show_list_(grouped_by_product)" +#~ msgstr "Liste anzeigen (gruppiert nach Produkt)" + +#~ msgid "Show_list_(grouped_by_member)" +#~ msgstr "Liste anzeigen (gruppiert nach Mitglied)" + +#~ msgid "Show_list_(anonymized)" +#~ msgstr "Liste anzeigen (anonymisiert)" + +#~ msgid "Delete_selected_products" +#~ msgstr "Ausgewählte Produkte löschen" + +#~ msgid "Edit_delivery_rhythm_for_selected_products" +#~ msgstr "Lieferrhythmus für ausgewählte Produkte bearbeiten" + +#~ msgid "Edit_status_for_selected_products" +#~ msgstr "Status für ausgewählte Produkte bearbeiten" + +#~ msgid "Software_update_version" +#~ msgstr "Software-Update / Version" + +#~ msgid "Please_find_more_information_in_the_changelog_{0}." +#~ msgstr "Mehr Informationen zu den Änderungen findest du im {0}." + +#~ msgid "Reminder" +#~ msgstr "Erinnerung" + +#, fuzzy +#~ msgid "Please_chose" +#~ msgstr "Bitte auswählen..." + #~ msgid "Please_enter_why_pickup_day_is_changed." #~ msgstr "Bitte gib an, warum der Abholtag geändert wird." @@ -2463,9 +2558,6 @@ msgstr "Produkt-Nr." #~ msgid "Zero_means_no_limit_and_global_limit_of_{0}_is_used." #~ msgstr "0 bedeutet: globales Limit von {0} wird verwendet." -#~ msgid "Delete" -#~ msgstr "Löschen" - #~ msgid "Additional_in_{0}" #~ msgstr "Zusätzlich in {0}" diff --git a/plugins/Admin/resources/locales/default.pot b/plugins/Admin/resources/locales/default.pot index 5b126bac2b..db3cedc228 100644 --- a/plugins/Admin/resources/locales/default.pot +++ b/plugins/Admin/resources/locales/default.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" -"POT-Creation-Date: 2022-08-18 17:24+0200\n" +"POT-Creation-Date: 2023-02-13 21:01+0100\n" "PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" "Last-Translator: NAME \n" "Language-Team: LANGUAGE \n" @@ -83,6 +83,15 @@ msgstr "" msgid "Test_email" msgstr "" +msgid "Cronjobs" +msgstr "" + +msgid "Edit_cronjob" +msgstr "" + +msgid "The_cronjob_{0}_has_been_changed." +msgstr "" + msgid "all_members" msgstr "" @@ -251,6 +260,15 @@ msgstr "" msgid "member" msgstr "" +msgid "Order_list_with_clear_names" +msgstr "" + +msgid "Anonymized_order_list" +msgstr "" + +msgid "Show_order_list" +msgstr "" + msgid "Order_lists" msgstr "" @@ -638,6 +656,12 @@ msgstr "" msgid "The_product_{0}_from_manufacturer_{1}_is_shown_as_new_from_now_on_for_the_next_{2}_days." msgstr "" +msgid "{0,plural,=1{1_product_was} other{#_products_were}}_deactivated." +msgstr "" + +msgid "{0,plural,=1{1_product_was} other{#_products_were}}_activated." +msgstr "" + msgid "The_product_{0}_from_manufacturer_{1}_was_deactivated." msgstr "" @@ -680,6 +704,12 @@ msgstr "" msgid "Turnover_statistics" msgstr "" +msgid "Total" +msgstr "" + +msgid "Last_{0}_months" +msgstr "" + msgid "December" msgstr "" @@ -914,12 +944,6 @@ msgstr "" msgid "Last_executed_migration" msgstr "" -msgid "Software_update_version" -msgstr "" - -msgid "Please_find_more_information_in_the_changelog_{0}." -msgstr "" - msgid "activated" msgstr "" @@ -941,6 +965,33 @@ msgstr "" msgid "Default_image_for_blog_post,_width:" msgstr "" +msgid "Website_administration" +msgstr "" + +msgid "Configurations" +msgstr "" + +msgid "Time_interval" +msgstr "" + +msgid "Day_of_month" +msgstr "" + +msgid "Please_select..." +msgstr "" + +msgid "Weekday" +msgstr "" + +msgid "Not_before_time" +msgstr "" + +msgid "Cronjob_is_called_up_to_10_min_after_the_given_time." +msgstr "" + +msgid "Last_run" +msgstr "" + msgid "Old_password" msgstr "" @@ -953,9 +1004,6 @@ msgstr "" msgid "New_password_again" msgstr "" -msgid "Website_administration" -msgstr "" - msgid "Financial_reports" msgstr "" @@ -1088,12 +1136,6 @@ msgstr "" msgid "Status" msgstr "" -msgid "Ordered_products" -msgstr "" - -msgid "Reminder" -msgstr "" - msgid "Feedback" msgstr "" @@ -1325,12 +1367,6 @@ msgstr "" msgid "Order_list_by_member" msgstr "" -msgid "Show_list_(grouped_by_product)" -msgstr "" - -msgid "Show_list_(grouped_by_member)" -msgstr "" - msgid "docs_route_manufacturers" msgstr "" @@ -1457,9 +1493,6 @@ msgstr "" msgid "How_do_I_use_the_function_delivery_break?" msgstr "" -msgid "Please_select..." -msgstr "" - msgid "Manufacturer_profile_and_products_are_only_visible_for_signed_in_members." msgstr "" @@ -1568,6 +1601,12 @@ msgstr "" msgid "Chose_member" msgstr "" +msgid "Anonymize_customers?" +msgstr "" + +msgid "Customer_data_(firstname_lastname_email)_will_either_be_hidden_or_anonymized_for_this_manufacturer." +msgstr "" + msgid "Pickup_days" msgstr "" @@ -1592,6 +1631,15 @@ msgstr "" msgid "Edit_manufacturer_settings" msgstr "" +msgid "grouped_by_product" +msgstr "" + +msgid "Anonymized_list" +msgstr "" + +msgid "grouped_by_customer" +msgstr "" + msgid "Test_order_list" msgstr "" @@ -1799,6 +1847,9 @@ msgstr "" msgid "Delivery_rhythm" msgstr "" +msgid "Selected_products" +msgstr "" + msgid "Storage_location" msgstr "" @@ -1838,7 +1889,7 @@ msgstr "" msgid "Gross_turnover" msgstr "" -msgid "Net_turnover" +msgid "Net_turnover_selling_price" msgstr "" msgid "total" @@ -1928,9 +1979,6 @@ msgstr "" msgid "BIC" msgstr "" -msgid "Configurations" -msgstr "" - msgid "My_products" msgstr "" @@ -1979,6 +2027,9 @@ msgstr "" msgid "Click_to_change_name" msgstr "" +msgid "Ordered_products" +msgstr "" + msgid "Show_all_ordered_products_from_{0}" msgstr "" @@ -2048,13 +2099,16 @@ msgstr "" msgid "Customer_ID" msgstr "" -msgid "Calculate_selling_price_with_surcharge_for_selected_products" +msgid "Calculate_selling_price" +msgstr "" + +msgid "Delete" msgstr "" -msgid "Delete_selected_products" +msgid "Edit_delivery_rhythm" msgstr "" -msgid "Edit_delivery_rhythm_for_selected_products" +msgid "Edit_status" msgstr "" msgid "Generate_product_cards" @@ -2294,6 +2348,9 @@ msgstr "" msgid "{0}_thanks_you_for_your_purchase!" msgstr "" +msgid "Pay_this_invoice_with_paypal" +msgstr "" + msgid "this_invoice_contains_all_products_that_have_been_delivered_in_{0}." msgstr "" @@ -2360,9 +2417,6 @@ msgstr "" msgid "Detail_view" msgstr "" -msgid "Total" -msgstr "" - msgid "The_delivered_weight_will_eventually_be_adapted_which_means_the_price_can_change_slightly." msgstr "" diff --git a/plugins/Admin/resources/locales/en_US/admin.mo b/plugins/Admin/resources/locales/en_US/admin.mo index b42370495c..a5466675b1 100644 Binary files a/plugins/Admin/resources/locales/en_US/admin.mo and b/plugins/Admin/resources/locales/en_US/admin.mo differ diff --git a/plugins/Admin/resources/locales/en_US/admin.po b/plugins/Admin/resources/locales/en_US/admin.po index 33d4659fc2..0761d29186 100644 --- a/plugins/Admin/resources/locales/en_US/admin.po +++ b/plugins/Admin/resources/locales/en_US/admin.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2022-08-18 17:24+0200\n" -"PO-Revision-Date: 2022-08-18 18:21+0200\n" +"POT-Creation-Date: 2023-02-13 21:01+0100\n" +"PO-Revision-Date: 2023-03-06 22:05+0100\n" "Last-Translator: \n" "Language-Team: LANGUAGE \n" "Language: en_US\n" @@ -13,7 +13,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.1.1\n" +"X-Generator: Poedit 3.2.2\n" msgid "Activities" msgstr "Activities" @@ -84,6 +84,15 @@ msgstr "Send test email" msgid "Test_email" msgstr "Test email" +msgid "Cronjobs" +msgstr "Cronjobs" + +msgid "Edit_cronjob" +msgstr "Edit cronjob" + +msgid "The_cronjob_{0}_has_been_changed." +msgstr "The cronjob {0} has been changed." + msgid "all_members" msgstr "all members" @@ -252,6 +261,15 @@ msgstr "Order list" msgid "member" msgstr "member" +msgid "Order_list_with_clear_names" +msgstr "Order list with member names" + +msgid "Anonymized_order_list" +msgstr "Anonymized order list" + +msgid "Show_order_list" +msgstr "Show order list" + msgid "Order_lists" msgstr "Order lists" @@ -639,6 +657,12 @@ msgstr "The product {0} from manufacturer {1} is not shown as \"new\" any more." msgid "The_product_{0}_from_manufacturer_{1}_is_shown_as_new_from_now_on_for_the_next_{2}_days." msgstr "The product {0} from manufacturer {1} is shown as \"new\" from now on for the next {2} days." +msgid "{0,plural,=1{1_product_was} other{#_products_were}}_deactivated." +msgstr "{0,plural,=1{One product was} other{# products were}} deactivated." + +msgid "{0,plural,=1{1_product_was} other{#_products_were}}_activated." +msgstr "{0,plural,=1{One product was} other{# products were}} activated." + msgid "The_product_{0}_from_manufacturer_{1}_was_deactivated." msgstr "The product {0} of manufacturer {1} has been deactivated." @@ -681,6 +705,12 @@ msgstr "Turnover and profit statistics" msgid "Turnover_statistics" msgstr "Turnover statistics" +msgid "Total" +msgstr "Total" + +msgid "Last_{0}_months" +msgstr "Last {0} months" + msgid "December" msgstr "December" @@ -915,12 +945,6 @@ msgstr "Version of FoodCoopShop" msgid "Last_executed_migration" msgstr "Last executed migration" -msgid "Software_update_version" -msgstr "Software update / Version" - -msgid "Please_find_more_information_in_the_changelog_{0}." -msgstr "Please find more information in the {0}." - msgid "activated" msgstr "activated" @@ -942,6 +966,33 @@ msgstr "Default image for manufacturer, width:" msgid "Default_image_for_blog_post,_width:" msgstr "Default image for blog post, width:" +msgid "Website_administration" +msgstr "Website administration" + +msgid "Configurations" +msgstr "Settings" + +msgid "Time_interval" +msgstr "Interval" + +msgid "Day_of_month" +msgstr "Day of month" + +msgid "Please_select..." +msgstr "Please select..." + +msgid "Weekday" +msgstr "Weekday" + +msgid "Not_before_time" +msgstr "Time" + +msgid "Cronjob_is_called_up_to_10_min_after_the_given_time." +msgstr "Cronjob is called up to 10 min after the given time." + +msgid "Last_run" +msgstr "Last run" + msgid "Old_password" msgstr "Old password" @@ -954,9 +1005,6 @@ msgstr "min 8 characters" msgid "New_password_again" msgstr "New password (again)" -msgid "Website_administration" -msgstr "Website administration" - msgid "Financial_reports" msgstr "Financial reports" @@ -1089,12 +1137,6 @@ msgstr "Group" msgid "Status" msgstr "Status" -msgid "Ordered_products" -msgstr "Ordered products" - -msgid "Reminder" -msgstr "Reminder" - msgid "Feedback" msgstr "Feedback" @@ -1332,12 +1374,6 @@ msgstr "Order list by product" msgid "Order_list_by_member" msgstr "Order list by member" -msgid "Show_list_(grouped_by_product)" -msgstr "Show list (grouped by product)" - -msgid "Show_list_(grouped_by_member)" -msgstr "Show list (grouped by member)" - msgid "docs_route_manufacturers" msgstr "manufacturers" @@ -1464,11 +1500,8 @@ msgstr "Delivery break" msgid "How_do_I_use_the_function_delivery_break?" msgstr "How do I use the function \"delivery break\"?" -msgid "Please_select..." -msgstr "Please select..." - msgid "Manufacturer_profile_and_products_are_only_visible_for_signed_in_members." -msgstr "Manufacturer provile and products are only visible for signed in members." +msgstr "Manufacturer profile and products are only visible for signed in members." msgid "Order_lists_by_email" msgstr "Order lists by email" @@ -1575,6 +1608,12 @@ msgstr "Should stock products be included in order lists?" msgid "Chose_member" msgstr "Chose member..." +msgid "Anonymize_customers?" +msgstr "Anonymize members?" + +msgid "Customer_data_(firstname_lastname_email)_will_either_be_hidden_or_anonymized_for_this_manufacturer." +msgstr "Anonymize member data like firstname, lastname, email for this manufacturer globally." + msgid "Pickup_days" msgstr "Pickup days" @@ -1599,6 +1638,15 @@ msgstr "Show deposit account" msgid "Edit_manufacturer_settings" msgstr "Edit manufacturer settings" +msgid "grouped_by_product" +msgstr "group by product" + +msgid "Anonymized_list" +msgstr "Anonymized list" + +msgid "grouped_by_customer" +msgstr "group by member" + msgid "Test_order_list" msgstr "Test order list" @@ -1806,6 +1854,9 @@ msgstr "New?" msgid "Delivery_rhythm" msgstr "Delivery rhythm" +msgid "Selected_products" +msgstr "Selected products" + msgid "Storage_location" msgstr "Storage location" @@ -1845,8 +1896,8 @@ msgstr "Net purchase price" msgid "Gross_turnover" msgstr "Gross turnover" -msgid "Net_turnover" -msgstr "Net turnover" +msgid "Net_turnover_selling_price" +msgstr "Net turnover (SP)" msgid "total" msgstr "total" @@ -1935,9 +1986,6 @@ msgstr "IBAN" msgid "BIC" msgstr "BIC" -msgid "Configurations" -msgstr "Settings" - msgid "My_products" msgstr "My products" @@ -1986,6 +2034,9 @@ msgstr "Give the manufacturer feedback for the product" msgid "Click_to_change_name" msgstr "Click to change name" +msgid "Ordered_products" +msgstr "Ordered products" + msgid "Show_all_ordered_products_from_{0}" msgstr "Show all ordered products from {0}" @@ -2055,14 +2106,17 @@ msgstr "Thank you very much for delivering your products to us!" msgid "Customer_ID" msgstr "Customer-ID" -msgid "Calculate_selling_price_with_surcharge_for_selected_products" -msgstr "Calculate selling price with surcharge for selected products" +msgid "Calculate_selling_price" +msgstr "Calculate selling price" -msgid "Delete_selected_products" -msgstr "Delete selected products" +msgid "Delete" +msgstr "Delete" -msgid "Edit_delivery_rhythm_for_selected_products" -msgstr "Edit delivery rhythm for selected products" +msgid "Edit_delivery_rhythm" +msgstr "Edit delivery rhythm" + +msgid "Edit_status" +msgstr "Edit status" msgid "Generate_product_cards" msgstr "Generate product cards" @@ -2301,6 +2355,9 @@ msgstr "Please find your invoice attached." msgid "{0}_thanks_you_for_your_purchase!" msgstr "Thank you for your purchase!" +msgid "Pay_this_invoice_with_paypal" +msgstr "Pay this invoice with paypal" + msgid "this_invoice_contains_all_products_that_have_been_delivered_in_{0}." msgstr "this invoice contains all products that have been delivered in {0}." @@ -2367,15 +2424,54 @@ msgstr "The total sum to the right (price incl.) will be transferred to your ban msgid "Detail_view" msgstr "Detail view" -msgid "Total" -msgstr "Total" - msgid "The_delivered_weight_will_eventually_be_adapted_which_means_the_price_can_change_slightly." msgstr "The delivered weight will eventually be adapted, so the price can change slightly." msgid "Product_ID" msgstr "ProductID" +#~ msgid "Net_turnover" +#~ msgstr "Net turnover" + +#~ msgid "List_with_clear_names" +#~ msgstr "List with clear names" + +#, fuzzy +#~ msgid "Anonymize_members" +#~ msgstr "Anonymize members?" + +#~ msgid "Show_list_(grouped_by_product)" +#~ msgstr "Show list (grouped by product)" + +#~ msgid "Show_list_(grouped_by_member)" +#~ msgstr "Show list (grouped by member)" + +#, fuzzy +#~ msgid "Show_list_(anonymized)" +#~ msgstr "Show list (grouped by member)" + +#~ msgid "Delete_selected_products" +#~ msgstr "Delete selected products" + +#~ msgid "Edit_delivery_rhythm_for_selected_products" +#~ msgstr "Edit delivery rhythm for selected products" + +#~ msgid "Edit_status_for_selected_products" +#~ msgstr "Edit status for selected products" + +#~ msgid "Software_update_version" +#~ msgstr "Software update / Version" + +#~ msgid "Please_find_more_information_in_the_changelog_{0}." +#~ msgstr "Please find more information in the {0}." + +#~ msgid "Reminder" +#~ msgstr "Reminder" + +#, fuzzy +#~ msgid "Please_chose" +#~ msgstr "Please select..." + #~ msgid "Please_enter_why_pickup_day_is_changed." #~ msgstr "Please enter why the pickup day is changed." @@ -2458,9 +2554,6 @@ msgstr "ProductID" #~ msgid "Zero_means_no_limit_and_global_limit_of_{0}_is_used." #~ msgstr "0 means: global limit of {0} is used." -#~ msgid "Delete" -#~ msgstr "Delete" - #~ msgid "Additional_in_{0}" #~ msgstr "Additional in {0}" diff --git a/plugins/Admin/src/Controller/ActionLogsController.php b/plugins/Admin/src/Controller/ActionLogsController.php index 90a7d3ed86..083926b98e 100644 --- a/plugins/Admin/src/Controller/ActionLogsController.php +++ b/plugins/Admin/src/Controller/ActionLogsController.php @@ -1,4 +1,6 @@ AppAuth->getUserId().') )'; + + if ($this->AppAuth->getManufacturerAnonymizeCustomers()) { + $conditions['ActionLogs.type NOT IN'] = $this->ActionLog->getHiddenTypesForManufacturersWithEnabledAnonymization(); + } + } // customers are only allowed to see their own data diff --git a/plugins/Admin/src/Controller/AdminAppController.php b/plugins/Admin/src/Controller/AdminAppController.php index ebd72d2779..04ab2a611d 100644 --- a/plugins/Admin/src/Controller/AdminAppController.php +++ b/plugins/Admin/src/Controller/AdminAppController.php @@ -1,11 +1,10 @@ set('referer', ! empty($this->getRequest()->getData('referer')) ? $this->getRequest()->getData('referer') : $this->referer()); } - /** - * deletes physical files (thumbs) - */ - protected function deleteUploadedImage($imageId, $thumbsPath) + protected function deleteUploadedImage(int $imageId, string $thumbsPath): void { - $dir = new Folder($thumbsPath); - $files = $dir->read(); - if (!empty($files[1])) { - foreach($files[1] as $file) { - if (preg_match('/^' . $imageId . '-/', $file)) { - $file = new File($thumbsPath . DS . $file); - $file->delete(); + $dir = new \DirectoryIterator($thumbsPath); + foreach ($dir as $fileinfo) { + if (!$fileinfo->isDot()) { + $filename = $fileinfo->getFilename(); + if (preg_match('/^' . $imageId . '-/', $filename)) { + unlink($thumbsPath . DS . $filename); } } } } - /** - * - * @param int $imageId - * @param string $filename - * @param string $thumbsPath - * @param array $imageSizes - */ - protected function saveUploadedImage($imageId, $filename, $thumbsPath, $imageSizes) + protected function saveUploadedImage(int $imageId, string $filename, string $thumbsPath, array $imageSizes): string { $this->deleteUploadedImage($imageId, $thumbsPath); @@ -82,5 +70,6 @@ protected function saveUploadedImage($imageId, $filename, $thumbsPath, $imageSiz } return $imageId . $options['suffix'] . '.' . $extension; + } } diff --git a/plugins/Admin/src/Controller/AttributesController.php b/plugins/Admin/src/Controller/AttributesController.php index c1922f9e10..bb0c8ff619 100644 --- a/plugins/Admin/src/Controller/AttributesController.php +++ b/plugins/Admin/src/Controller/AttributesController.php @@ -1,4 +1,6 @@ set('manufacturersForDropdown', $this->Manufacturer->getForDropdown()); $_SESSION['ELFINDER'] = [ - 'uploadUrl' => Configure::read('app.cakeServerName') . "/files/kcfinder/blog_posts", + 'uploadUrl' => Configure::read('App.fullBaseUrl') . "/files/kcfinder/blog_posts", 'uploadPath' => $_SERVER['DOCUMENT_ROOT'] . "/files/kcfinder/blog_posts" ]; diff --git a/plugins/Admin/src/Controller/CategoriesController.php b/plugins/Admin/src/Controller/CategoriesController.php index 1c5e4c5eeb..68293a07b3 100644 --- a/plugins/Admin/src/Controller/CategoriesController.php +++ b/plugins/Admin/src/Controller/CategoriesController.php @@ -1,4 +1,6 @@ type, ['textarea_big'])) { $_SESSION['ELFINDER'] = [ - 'uploadUrl' => Configure::read('app.cakeServerName') . "/files/kcfinder/configurations/", + 'uploadUrl' => Configure::read('App.fullBaseUrl') . "/files/kcfinder/configurations/", 'uploadPath' => $_SERVER['DOCUMENT_ROOT'] . "/files/kcfinder/configurations/" ]; } @@ -175,7 +177,7 @@ public function index() $this->set('versionFoodCoopShop', $this->Configuration->getVersion()); try { - $query = 'SELECT * FROM phinxlog ORDER by version DESC LIMIT 1;'; + $query = 'SELECT * FROM phinxlog WHERE start_time IS NOT NULL ORDER by version DESC LIMIT 1;'; $lastMigration = $this->Configuration->getConnection()->query($query)->fetch('assoc'); $this->set('lastMigration', $lastMigration); } catch (\PDOException $e) { diff --git a/plugins/Admin/src/Controller/CronjobsController.php b/plugins/Admin/src/Controller/CronjobsController.php new file mode 100644 index 0000000000..0a1f1f7b5a --- /dev/null +++ b/plugins/Admin/src/Controller/CronjobsController.php @@ -0,0 +1,104 @@ + + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +class CronjobsController extends AdminAppController +{ + + public function isAuthorized($user) + { + return $this->AppAuth->isSuperadmin(); + } + + public function index() + { + $this->Cronjobs = $this->getTableLocator()->get('Cronjobs'); + $cronjobs = $this->Cronjobs->find('available'); + + $cronjobs->contain([ + 'CronjobLogs' => function (Query $q) { + $q->orderDesc('CronjobLogs.created'); + return $q; + } + ]); + + $this->set('cronjobs', $cronjobs); + $this->set('title_for_layout', __d('admin', 'Cronjobs')); + } + + public function edit($cronjobId) + { + $this->Cronjob = $this->getTableLocator()->get('Cronjobs'); + $cronjob = $this->Cronjob->find('available', [ + 'conditions' => [ + 'Cronjobs.id' => $cronjobId, + ] + ])->first(); + + if (empty($cronjob)) { + throw new NotFoundException; + } + $this->set('title_for_layout', __d('admin', 'Edit_cronjob')); + + $this->setFormReferer(); + $this->set('timeIntervals', $this->Cronjob->getTimeIntervals()); + $this->set('daysOfMonth', $this->Cronjob->getDaysOfMonth()); + $this->set('weekdays', $this->Cronjob->getWeekdays()); + + if (empty($this->getRequest()->getData())) { + $this->set('cronjob', $cronjob); + return; + } + + $this->loadComponent('Sanitize'); + $this->setRequest($this->getRequest()->withParsedBody($this->Sanitize->trimRecursive($this->getRequest()->getData()))); + $this->setRequest($this->getRequest()->withParsedBody($this->Sanitize->stripTagsAndPurifyRecursive($this->getRequest()->getData()))); + + $validationName = $cronjob->getOriginalValues()['name']; + if (!method_exists($this->Cronjob, 'validation'.$validationName)) { + $validationName = 'default'; + } + + $cronjob = $this->Cronjob->patchEntity( + $cronjob, + $this->getRequest()->getData(), + [ + 'validate' => $validationName, + ], + ); + if ($cronjob->hasErrors()) { + $this->Flash->error(__d('admin', 'Errors_while_saving!')); + $this->set('cronjob', $cronjob); + } else { + $cronjob = $this->Cronjob->save($cronjob); + $this->ActionLog = $this->getTableLocator()->get('ActionLogs'); + $message = __d('admin', 'The_cronjob_{0}_has_been_changed.', ['' . $cronjob->name . '']); + $this->ActionLog->customSave('cronjob_changed', $this->AppAuth->getUserId(), $cronjob->id, 'cronjobs', $message); + $this->Flash->success($message); + + $this->redirect($this->getPreparedReferer()); + } + + $this->set('cronjob', $cronjob); + + } + +} diff --git a/plugins/Admin/src/Controller/CustomersController.php b/plugins/Admin/src/Controller/CustomersController.php index 1f19be4672..aff4b1c1b9 100644 --- a/plugins/Admin/src/Controller/CustomersController.php +++ b/plugins/Admin/src/Controller/CustomersController.php @@ -1,4 +1,6 @@ getRequest()->getParam('action')) { - case 'generateMemberCards': - return Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED') && ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin()); - break; - case 'generateMyMemberCard': - return Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED') && ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isCustomer()); - break; - case 'edit': - case 'creditBalanceSum': - return $this->AppAuth->isSuperadmin(); - break; - case 'profile': - return $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isCustomer(); - break; - case 'delete': - return $this->AppAuth->isSuperadmin(); - break; - case 'changePassword': - case 'ajaxGetCustomersForDropdown': - return $this->AppAuth->user(); - break; - default: - return $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'generateMemberCards' => Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED') && ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin()), + 'generateMyMemberCard' => Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED') && ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isCustomer()), + 'creditBalanceSum', 'delete' => $this->AppAuth->isSuperadmin(), + 'profile' => $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isCustomer(), + 'changePassword', 'ajaxGetCustomersForDropdown' => $this->AppAuth->user(), + default => $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(), + }; } public function ajaxGetCustomersForDropdown($includeManufacturers, $includeOfflineCustomers = true) @@ -74,6 +59,7 @@ public function ajaxGetCustomersForDropdown($includeManufacturers, $includeOffli if ($this->AppAuth->isSuperadmin()) { $includeOfflineCustomers = true; } + $customers = $this->Customer->getForDropdown($includeManufacturers, $includeOfflineCustomers, $conditions); $customersForDropdown = []; foreach ($customers as $key => $ps) { @@ -265,7 +251,7 @@ public function changePassword() } - public function delete($customerId) + public function delete(int $customerId) { $this->RequestHandler->renderAs($this, 'json'); @@ -414,6 +400,10 @@ private function _processForm($customerId) ] ])->first(); + if (empty($customer)) { + throw new NotFoundException('customer not found'); + } + $this->setFormReferer(); if (empty($this->getRequest()->getData())) { @@ -629,7 +619,7 @@ public function creditBalanceSum() 'customer_type' => __d('admin', 'Sum_of_credits_of_deleted_members'), 'count' => 0, 'credit_balance' => $paymentProductDelta + $paymentDepositDelta, - 'payment_deposit_delta' => $paymentDepositDelta * -1 + 'payment_deposit_delta' => ($paymentDepositDelta * -1) + 0, ]; $paymentDepositDelta = $this->Payment->getManufacturerDepositMoneySum(); @@ -637,7 +627,7 @@ public function creditBalanceSum() 'customer_type' => __d('admin', 'Sum_of_deposit_compensation_payments_for_manufactures'), 'count' => 0, 'credit_balance' => 0, - 'payment_deposit_delta' => $paymentDepositDelta * -1 + 'payment_deposit_delta' => ($paymentDepositDelta * -1) + 0, ]; $this->set('customers', $customers); @@ -652,6 +642,7 @@ public function creditBalanceSum() $sums['deposit_delta'] += $customer['payment_deposit_delta'] ?? 0; $sums['product_delta'] += $customer['payment_product_delta'] ?? 0; } + $this->set('sums', $sums); $this->set('title_for_layout', __d('admin', 'Credit_and_deposit_balance')); @@ -671,7 +662,19 @@ public function index() } $this->set('year', $year); - $this->set('years', Configure::read('app.timeHelper')->getAllYearsUntilThisYear(date('Y'), 2017, __d('admin', 'Member_fee') . ' ')); + $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); + + $firstOrderYear = $this->OrderDetail->getFirstOrderYear(); + $this->set('firstOrderYear', $firstOrderYear); + + $lastOrderYear = $this->OrderDetail->getLastOrderYear(); + $this->set('lastOrderYear', $lastOrderYear); + + $years = null; + if ($lastOrderYear !== false && $firstOrderYear !== false) { + $years = Configure::read('app.timeHelper')->getAllYearsUntilThisYear($lastOrderYear, $firstOrderYear, __d('admin', 'Member_fee') . ' '); + } + $this->set('years', $years); $conditions = []; if ($active != 'all') { @@ -713,6 +716,13 @@ public function index() if (Configure::read('appDb.FCS_USER_FEEDBACK_ENABLED')) { $query->select($this->Customer->Feedbacks); } + + $query->select([ + 'credit_balance' => 'Customers.id_customer', // add fake field to make custom sort icon work and avoid "Column not found: 1054 Unknown column" + 'last_pickup_day' => 'Customers.id_customer', + 'member_fee' => 'Customers.id_customer', + ]); + $query->select($this->Customer->AddressCustomers); $customers = $this->paginate($query, [ 'sortableFields' => [ 'CustomerNameForOrder', @@ -721,31 +731,44 @@ public function index() 'Customers.email', 'Customers.active', 'Customers.email_order_reminder_enabled', + 'Customers.check_credit_reminder_enabled', 'Customers.date_add', 'Customers.newsletter_enabled', 'Feedbacks.modified', + 'credit_balance', + 'member_fee', + 'last_pickup_day', ], 'order' => $this->Customer->getCustomerOrderClause(), ])->toArray(); $i = 0; $this->Payment = $this->getTableLocator()->get('Payments'); - $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); foreach ($customers as $customer) { if (Configure::read('app.htmlHelper')->paymentIsCashless()) { $customer->credit_balance = $this->Customer->getCreditBalance($customer->id_customer); } - $customer->order_detail_count = $this->OrderDetail->getCountByCustomerId($customer->id_customer); $customer->different_pickup_day_count = $this->OrderDetail->getDifferentPickupDayCountByCustomerId($customer->id_customer); - $customer->last_order_date = $this->OrderDetail->getLastOrderDate($customer->id_customer); + $customer->last_pickup_day = $this->OrderDetail->getLastPickupDay($customer->id_customer); + $customer->last_pickup_day_sort = ''; + if (!is_null($customer->last_pickup_day)) { + $customer->last_pickup_day_sort = $customer->last_pickup_day->pickup_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); + } $customer->member_fee = $this->OrderDetail->getMemberFee($customer->id_customer, $year); $i ++; } - if (in_array('sort', array_keys($this->getRequest()->getQueryParams())) && $this->getRequest()->getQuery('sort') == 'Customers.member_fee') { - $customers = Hash::sort($customers, '{n}.member_fee', $this->getRequest()->getQuery('direction'), [ - 'type' => 'locale', + if (in_array('sort', array_keys($this->getRequest()->getQueryParams())) + && in_array($this->getRequest()->getQuery('sort'), ['credit_balance', 'member_fee', 'last_pickup_day',])) { + $path = '{n}.' .$this->getRequest()->getQuery('sort'); + $type = 'numeric'; + if ($this->getRequest()->getQuery('sort') == 'last_pickup_day') { + $path .= '_sort'; + $type = 'locale'; + } + $customers = Hash::sort($customers, $path, $this->getRequest()->getQuery('direction'), [ + 'type' => $type, 'ignoreCase' => true, ]); } diff --git a/plugins/Admin/src/Controller/DepositsController.php b/plugins/Admin/src/Controller/DepositsController.php index d4bb5313c1..862c7e9833 100644 --- a/plugins/Admin/src/Controller/DepositsController.php +++ b/plugins/Admin/src/Controller/DepositsController.php @@ -1,4 +1,6 @@ getRequest()->getParam('action')) { - case 'overviewDiagram': - return Configure::read('app.isDepositEnabled') && $this->AppAuth->isSuperadmin(); - break; - case 'index': - case 'detail': - return Configure::read('app.isDepositEnabled') && $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(); - break; - case 'myIndex': - case 'myDetail': - return Configure::read('app.isDepositEnabled') && $this->AppAuth->isManufacturer(); - break; - default: - return Configure::read('app.isDepositEnabled') && $this->AppAuth->isManufacturer(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'overviewDiagram' => Configure::read('app.isDepositEnabled') && $this->AppAuth->isSuperadmin(), + 'index', 'detail' => Configure::read('app.isDepositEnabled') && $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(), + 'myIndex', 'myDetail' => Configure::read('app.isDepositEnabled') && $this->AppAuth->isManufacturer(), + default => Configure::read('app.isDepositEnabled') && $this->AppAuth->isManufacturer(), + }; } /** diff --git a/plugins/Admin/src/Controller/FeedbacksController.php b/plugins/Admin/src/Controller/FeedbacksController.php index c1deb41b4c..b806e6b2da 100644 --- a/plugins/Admin/src/Controller/FeedbacksController.php +++ b/plugins/Admin/src/Controller/FeedbacksController.php @@ -1,4 +1,6 @@ getRequest()->getParam('action')) { - case 'myFeedback': - return Configure::read('appDb.FCS_USER_FEEDBACK_ENABLED') && $this->AppAuth->user(); - break; - default: - return Configure::read('appDb.FCS_USER_FEEDBACK_ENABLED') && $this->AppAuth->isSuperadmin(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'myFeedback' => Configure::read('appDb.FCS_USER_FEEDBACK_ENABLED') && $this->AppAuth->user(), + default => Configure::read('appDb.FCS_USER_FEEDBACK_ENABLED') && $this->AppAuth->isSuperadmin(), + }; } private function getCustomerId() diff --git a/plugins/Admin/src/Controller/HelloCashController.php b/plugins/Admin/src/Controller/HelloCashController.php index 9763ed8db7..c19fc4a581 100644 --- a/plugins/Admin/src/Controller/HelloCashController.php +++ b/plugins/Admin/src/Controller/HelloCashController.php @@ -1,4 +1,5 @@ getRequest()->getParam('action')) { - case 'getInvoice': - return (Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && $this->AppAuth->user()) || - ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isManufacturer()); - break; - default: - return $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isManufacturer(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'getInvoice' => (Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && $this->AppAuth->user()) || + ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isManufacturer()), + default => $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin() || $this->AppAuth->isManufacturer(), + }; } public function orderLists() @@ -45,7 +43,7 @@ public function orderLists() if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { $dateFrom = Configure::read('app.timeHelper')->formatToDateShort(Configure::read('app.timeHelper')->getCurrentDateForDatabase()); } else { - $dateFrom = Configure::read('app.timeHelper')->getFormattedNextDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); + $dateFrom = DeliveryRhythm::getFormattedNextDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); } if (! empty($this->getRequest()->getQuery('dateFrom'))) { @@ -56,59 +54,93 @@ public function orderLists() $files = []; foreach ($objects as $name => $object) { - if (preg_match('/\.pdf$/', $name)) { - // before 09/2017 ProductLists were generated and stored with "Artikel" in filename - // the following preg_match avoids a batch renaming - if (!preg_match('/'.__d('admin', '_Order_list_filename_').'('.__d('admin', 'product').'|Artikel)/', $name, $matches)) { - continue; - } + if (!preg_match('/\.pdf$/', $name)) { + continue; + } - $splittedFileName = $this->splitOrderDetailStringIntoParts($object->getFileName(), $matches[1]); - $deliveryDate = $splittedFileName['deliveryDate']; - $manufacturerId = $splittedFileName['manufacturerId']; + // before 09/2017 ProductLists were generated and stored with "Artikel" in filename + // the following preg_match avoids a batch renaming + if (!preg_match('/'.__d('admin', '_Order_list_filename_').'('.__d('admin', 'product').'|Artikel)/', $name, $matches)) { + continue; + } + + $splittedFileName = $this->splitOrderDetailStringIntoParts($object->getFileName(), $matches[1]); + $deliveryDate = $splittedFileName['deliveryDate']; + $manufacturerId = $splittedFileName['manufacturerId']; + $generationDate = $splittedFileName['generationDate']; + + // date check + if (! (strtotime($dateFrom) == strtotime($deliveryDate))) { + continue; + } + + if ($this->AppAuth->isManufacturer() && $manufacturerId != $this->AppAuth->getManufacturerId()) { + continue; + } + + $isAnonymized = $this->isAnonymized($name); - // date check - if (! (strtotime($dateFrom) == strtotime($deliveryDate))) { + if ($this->AppAuth->isManufacturer()) { + if ($this->AppAuth->getManufacturerAnonymizeCustomers() && !$isAnonymized) { continue; } - - if ($this->AppAuth->isManufacturer() && $manufacturerId != $this->AppAuth->getManufacturerId()) { + if (!$this->AppAuth->getManufacturerAnonymizeCustomers() && $isAnonymized) { continue; } + } - if (!$manufacturerId) { - $message = 'error: ManufacturerId not found in ' . $object->getFileName(); - $this->Flash->error($message); - $this->log($message); - $this->set('files', []); - return; - } + if (!$manufacturerId) { + $message = 'error: ManufacturerId not found in ' . $object->getFileName(); + $this->Flash->error($message); + $this->log($message); + $this->set('files', []); + return; + } - $manufacturer = $this->Manufacturer->find('all', [ - 'conditions' => [ - 'Manufacturers.id_manufacturer' => $manufacturerId - ] - ])->first(); - - $productListLink = '/admin/lists/getOrderList?file=' . str_replace(Configure::read('app.folder_order_lists') . DS, '', $name); - $productListLink = str_replace(DS, '/', $productListLink); - $customerListLink = preg_replace( - '/' . str_replace(' ', '_', __d('admin', 'Order_list')) . '_' . $matches[1] . '/', - str_replace(' ', '_', __d('admin', 'Order_list')) . '_' . __d('admin', 'member'), - $productListLink, - 1); - - $files[] = [ - 'delivery_date' => $deliveryDate, - 'manufacturer_name' => $manufacturer->name, - 'product_list_link' => $productListLink, - 'customer_list_link' => $customerListLink - ]; - - $files = Hash::sort($files, '{n}.manufacturer_name', 'asc'); + $manufacturer = $this->Manufacturer->find('all', [ + 'conditions' => [ + 'Manufacturers.id_manufacturer' => $manufacturerId + ] + ])->first(); + + $productListLink = '/admin/lists/getOrderList?file=' . str_replace(Configure::read('app.folder_order_lists') . DS, '', $name); + $productListLink = str_replace(DS, '/', $productListLink); + $customerListLink = preg_replace( + '/' . str_replace(' ', '_', __d('admin', 'Order_list')) . '_' . $matches[1] . '/', + str_replace(' ', '_', __d('admin', 'Order_list')) . '_' . __d('admin', 'member'), + $productListLink, + 1); + + $listLabel = __d('admin', 'Order_list_with_clear_names'); + $listIcon = 'fa-eye'; + if ($isAnonymized) { + $listLabel = __d('admin', 'Anonymized_order_list'); + $listIcon = 'fa-eye-slash'; + } + if ($this->AppAuth->isManufacturer()) { + $listLabel = __d('admin', 'Show_order_list'); + $listIcon = 'fa-arrow-right'; } + $files[] = [ + 'delivery_date' => $deliveryDate, + 'manufacturer_name' => $manufacturer->name, + 'product_lists' => [ + ['label' => $listLabel, 'link' => $productListLink, 'icon' => $listIcon], + ], + 'customer_lists' => [ + ['label' => $listLabel, 'link' => $customerListLink, 'icon' => $listIcon], + ], + 'generation_date' => $generationDate, + 'manufacturer_id' => $manufacturerId, + 'is_anonymized' => $isAnonymized, + ]; + } + + $files = Hash::sort($files, '{n}.product_lists.0.label', 'asc'); + $files = Hash::sort($files, '{n}.manufacturer_name', 'asc'); + $this->set('files', $files); $this->set('title_for_layout', __d('admin', 'Order_lists')); @@ -128,10 +160,16 @@ private function splitOrderDetailStringIntoParts($fileName, $ending) $splittedManufacturerString = explode('_', $manufacturerString); $result['manufacturerId'] = (int) end($splittedManufacturerString); + $result['generationDate'] = substr($fileName, -23, 19); return $result; } + private function isAnonymized($path) + { + return preg_match('/anonymized/', $path); + } + public function getOrderList() { $filenameWithPath = Configure::read('app.folder_order_lists') . DS . h($this->getRequest()->getQuery('file')); @@ -142,7 +180,13 @@ public function getOrderList() $splittedFileName = $this->splitOrderDetailStringIntoParts(h($this->getRequest()->getQuery('file')), $matches[1]); $manufacturerId = $splittedFileName['manufacturerId']; if ($manufacturerId != $this->AppAuth->getManufacturerId()) { - throw new UnauthorizedException(); + throw new UnauthorizedException('manufacturer is not allowed to open order list of other manufacturers'); + } + if ($this->AppAuth->getManufacturerAnonymizeCustomers() && !$this->isAnonymized($filenameWithPath)) { + throw new UnauthorizedException('manufacturer is not allowed to open order list with clear text data'); + } + if (!$this->AppAuth->getManufacturerAnonymizeCustomers() && $this->isAnonymized($filenameWithPath)) { + throw new UnauthorizedException('manufacturer is not allowed to open order list with anonymized data'); } } } diff --git a/plugins/Admin/src/Controller/ManufacturersController.php b/plugins/Admin/src/Controller/ManufacturersController.php index 9f67f6d42c..178da06567 100644 --- a/plugins/Admin/src/Controller/ManufacturersController.php +++ b/plugins/Admin/src/Controller/ManufacturersController.php @@ -1,4 +1,5 @@ getRequest()->getParam('action')) { - case 'profile': - case 'myOptions': - return $this->AppAuth->isManufacturer(); - break; - case 'index': - case 'add': - return $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(); - break; - case 'edit': - case 'editOptions': - return $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(); - break; - case 'getDeliveryNote': - return Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED') && $this->AppAuth->isSuperadmin(); - break; - case 'getInvoice': - return !Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED') && !Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin()); - break; - default: - return $this->AppAuth->user(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'profile', 'myOptions' => $this->AppAuth->isManufacturer(), + 'index', 'add' => $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(), + 'edit', 'editOptions', 'getOrderListByProduct', 'getOrderListByCustomer', 'getInvoice' => + $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(), + 'getDeliveryNote' => Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED') && $this->AppAuth->isSuperadmin(), + 'getInvoice' => !Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED') && !Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && ($this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin()), + default => $this->AppAuth->user(), + }; } public function beforeFilter(EventInterface $event) @@ -80,6 +66,7 @@ public function add() [ 'active' => APP_ON, 'is_private' => Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') ? APP_OFF : APP_ON, + 'anonymize_customers' => APP_ON, ], ['validate' => false] ); @@ -98,7 +85,7 @@ public function edit($manufacturerId) } $_SESSION['ELFINDER'] = [ - 'uploadUrl' => Configure::read('app.cakeServerName') . "/files/kcfinder/manufacturers/" . $manufacturerId, + 'uploadUrl' => Configure::read('App.fullBaseUrl') . "/files/kcfinder/manufacturers/" . $manufacturerId, 'uploadPath' => $_SERVER['DOCUMENT_ROOT'] . "/files/kcfinder/manufacturers/" . $manufacturerId ]; @@ -215,20 +202,12 @@ private function _processForm($manufacturer, $isEditMode) $this->set('manufacturer', $manufacturer); } - private function saveUploadedGeneralTermsAndConditions($manufacturerId, $filename) + private function saveUploadedGeneralTermsAndConditions(int $manufacturerId, string $filename): void { - - $newFileName = Configure::read('app.htmlHelper')->getManufacturerTermsOfUseSrcTemplate($manufacturerId); - - $fileObject = new File(WWW_ROOT . $filename); - - // assure that folder structure exists - $dir = new Folder(); - $path = dirname(WWW_ROOT . $newFileName); - $dir->create($path); - $dir->chmod($path, 0755); - - $fileObject->copy(WWW_ROOT . $newFileName); + $newFilename = Configure::read('app.htmlHelper')->getManufacturerTermsOfUseSrcTemplate($manufacturerId); + $path = dirname(WWW_ROOT . $newFilename); + mkdir($path, 0755, true); + copy(WWW_ROOT . $filename, WWW_ROOT . $newFilename); } private function deleteUploadedGeneralTermsAndConditions($manufacturerId) @@ -255,7 +234,7 @@ public function setElFinderUploadPath($manufacturerId) } $_SESSION['ELFINDER'] = [ - 'uploadUrl' => Configure::read('app.cakeServerName') . "/files/kcfinder/manufacturers/" . $manufacturerId, + 'uploadUrl' => Configure::read('App.fullBaseUrl') . "/files/kcfinder/manufacturers/" . $manufacturerId, 'uploadPath' => $_SERVER['DOCUMENT_ROOT'] . "/files/kcfinder/manufacturers/" . $manufacturerId ]; @@ -272,7 +251,7 @@ private function getDefaultDate() { if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { $defaultDate = Configure::read('app.timeHelper')->formatToDateShort(Configure::read('app.timeHelper')->getCurrentDateForDatabase()); } else { - $defaultDate = Configure::read('app.timeHelper')->getFormattedNextDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); + $defaultDate = DeliveryRhythm::getFormattedNextDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); } return $defaultDate; } @@ -436,7 +415,7 @@ public function editOptions($manufacturerId) $this->set('taxesForDropdown', $this->Tax->getForDropdown()); if (!Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { - $noDeliveryBreakOptions = Configure::read('app.timeHelper')->getNextWeeklyDeliveryDays(); + $noDeliveryBreakOptions = DeliveryRhythm::getNextWeeklyDeliveryDays(); $this->set('noDeliveryBreakOptions', $noDeliveryBreakOptions); } @@ -604,7 +583,7 @@ public function getInvoice() $newInvoiceNumber = 'xxx'; $pdfWriter = new InvoiceToManufacturerPdfWriter(); - $pdfWriter->prepareAndSetData($manufacturerId, $dateFrom, $dateTo, $newInvoiceNumber, [], '', 'xxx'); + $pdfWriter->prepareAndSetData($manufacturerId, $dateFrom, $dateTo, $newInvoiceNumber, [], '', 'xxx', $manufacturer->anonymize_customers); if (isset($pdfWriter->getData()['productResults']) && empty($pdfWriter->getData()['productResults'])) { die(__d('admin', 'No_orders_within_the_given_time_range.')); } @@ -626,7 +605,7 @@ public function getInvoice() private function getOrderListFilenameForWriteInline($manufacturerId, $manufacturerName, $pickupDay, $type): string { $currentDateForOrderLists = Configure::read('app.timeHelper')->getCurrentDateTimeForFilename(); - $productPdfFile = Configure::read('app.htmlHelper')->getOrderListLink($manufacturerName, $manufacturerId, $pickupDay, $type, $currentDateForOrderLists); + $productPdfFile = Configure::read('app.htmlHelper')->getOrderListLink($manufacturerName, $manufacturerId, $pickupDay, $type, $currentDateForOrderLists, false); $productPdfFile = explode(DS, $productPdfFile); $productPdfFile = end($productPdfFile); $productPdfFile = substr($productPdfFile, 11); @@ -660,6 +639,12 @@ protected function getOrderList($type, $pdfWriter) ], ])->first(); + if (!in_array('isAnonymized', array_keys($this->getRequest()->getQueryParams()))) { + $isAnonymized = $manufacturer->anonymize_customers; + } else { + $isAnonymized = h($this->getRequest()->getQuery('isAnonymized')); + } + $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); $orderDetails = $this->OrderDetail->getOrderDetailsForOrderListPreview($pickupDayDbFormat); $orderDetails->where(['Products.id_manufacturer' => $manufacturerId]); @@ -679,7 +664,7 @@ protected function getOrderList($type, $pdfWriter) $pdfFile = $this->getOrderListFilenameForWriteInline($manufacturerId, $manufacturer->name, $pickupDay, $typeString); $pdfWriter->setFilename($pdfFile); - $pdfWriter->prepareAndSetData($manufacturerId, $pickupDayDbFormat, [], $orderDetailIds); + $pdfWriter->prepareAndSetData($manufacturerId, $pickupDayDbFormat, [], $orderDetailIds, $isAnonymized); if (!empty($this->request->getQuery('outputType')) && $this->request->getQuery('outputType') == 'html') { return $this->response->withStringBody($pdfWriter->writeHtml()); } diff --git a/plugins/Admin/src/Controller/OrderDetailsController.php b/plugins/Admin/src/Controller/OrderDetailsController.php index 420fd450c3..7f5bea1d3e 100644 --- a/plugins/Admin/src/Controller/OrderDetailsController.php +++ b/plugins/Admin/src/Controller/OrderDetailsController.php @@ -1,18 +1,20 @@ parseFloatRespectingLocale(Configure::read('appDb.FCS_DEPOSIT_TAX_RATE')); - $depositVatRate = Configure::read('app.numberHelper')->formatTaxRate($depositVatRate); + $depositTaxRate = Configure::read('app.numberHelper')->parseFloatRespectingLocale(Configure::read('appDb.FCS_DEPOSIT_TAX_RATE')); + $formattedDepositTaxRate = Configure::read('app.numberHelper')->formatTaxRate($depositTaxRate); foreach($preparedOrderDetails as $customerId => $orderDetails) { @@ -316,12 +318,12 @@ public function orderDetailsAsPdf() ]; foreach($orderDetails as $orderDetail) { - if (!isset($taxRates[$customerId][$depositVatRate])) { - $taxRates[$customerId][$depositVatRate] = $defaultArray; + if (!isset($taxRates[$customerId][$formattedDepositTaxRate])) { + $taxRates[$customerId][$formattedDepositTaxRate] = $defaultArray; } - $taxRates[$customerId][$depositVatRate]['sum_price_excl'] += $this->OrderDetail->getDepositNet($orderDetail->deposit, $orderDetail->product_amount); - $taxRates[$customerId][$depositVatRate]['sum_tax'] += $this->OrderDetail->getDepositTax($orderDetail->deposit, $orderDetail->product_amount); - $taxRates[$customerId][$depositVatRate]['sum_price_incl'] += $orderDetail->deposit; + $taxRates[$customerId][$formattedDepositTaxRate]['sum_price_excl'] += $this->OrderDetail->getDepositNet($orderDetail->deposit, $orderDetail->product_amount, $depositTaxRate); + $taxRates[$customerId][$formattedDepositTaxRate]['sum_tax'] += $this->OrderDetail->getDepositTax($orderDetail->deposit, $orderDetail->product_amount, $depositTaxRate); + $taxRates[$customerId][$formattedDepositTaxRate]['sum_price_incl'] += $orderDetail->deposit; } $taxRates[$customerId] = $this->OrderDetail->clearZeroArray($taxRates[$customerId]); @@ -369,7 +371,7 @@ public function editPurchasePrice($orderDetailId) } if (empty($this->getRequest()->getData())) { - $orderDetail->order_detail_purchase_price->total_price_tax_excl = round($orderDetail->order_detail_purchase_price->total_price_tax_excl, 2); + $orderDetail->order_detail_purchase_price->total_price_tax_excl = round((float) $orderDetail->order_detail_purchase_price->total_price_tax_excl, 2); $this->set('orderDetail', $orderDetail); return; } @@ -393,11 +395,11 @@ public function editPurchasePrice($orderDetailId) $this->Product = $this->getTableLocator()->get('Products'); $grossPrice = $this->Product->getGrossPrice( - round($orderDetail->order_detail_purchase_price->total_price_tax_excl, 2), + round((float) $orderDetail->order_detail_purchase_price->total_price_tax_excl, 2), $orderDetail->order_detail_purchase_price->tax_rate, ); - $unitPriceExcl = round($orderDetail->order_detail_purchase_price->total_price_tax_excl, 2) / $orderDetail->product_amount; + $unitPriceExcl = round((float) $orderDetail->order_detail_purchase_price->total_price_tax_excl, 2) / $orderDetail->product_amount; $unitTaxAmount = $this->Product->getUnitTax( $grossPrice, $unitPriceExcl, @@ -514,8 +516,8 @@ public function profit() foreach($orderDetails as $orderDetail) { $orderDetails[$i]->purchase_price_ok = false; if (!empty($orderDetail->order_detail_purchase_price)) { - $roundedPurchasePrice = round($orderDetail->order_detail_purchase_price->total_price_tax_excl, 2); - $roundedSellingPrice = round($orderDetail->total_price_tax_excl, 2); + $roundedPurchasePrice = round((float) $orderDetail->order_detail_purchase_price->total_price_tax_excl, 2); + $roundedSellingPrice = round((float) $orderDetail->total_price_tax_excl, 2); $roundedProfit = round($roundedSellingPrice - $roundedPurchasePrice, 2); if ($roundedPurchasePrice >= 0) { $orderDetails[$i]->purchase_price_ok = true; @@ -572,7 +574,7 @@ public function index() if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { $pickupDay[0] = Configure::read('app.timeHelper')->formatToDateShort(Configure::read('app.timeHelper')->getCurrentDateForDatabase()); } else { - $pickupDay[0] = Configure::read('app.timeHelper')->getFormattedNextDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); + $pickupDay[0] = DeliveryRhythm::getFormattedNextDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); } } } @@ -813,10 +815,7 @@ private function prepareGroupedOrderDetails($orderDetails, $groupBy) foreach ($orderDetails as $orderDetail) { $orderDetail->quantityInUnitsNotYetChanged = false; if (!empty($orderDetail->order_detail_unit)) { - // quantity comparison can be removed in v4. it was replaced by mark_as_saved in v3.1. default value needs to be set to true then - if (round($orderDetail->order_detail_unit->product_quantity_in_units, 3) == round($orderDetail->order_detail_unit->quantity_in_units * $orderDetail->product_amount, 3)) { - $orderDetail->quantityInUnitsNotYetChanged = true; - } + $orderDetail->quantityInUnitsNotYetChanged = true; if ($orderDetail->order_detail_unit->mark_as_saved) { $orderDetail->quantityInUnitsNotYetChanged = false; } @@ -866,6 +865,7 @@ public function editCustomer() $customerId = (int) $this->getRequest()->getData('customerId'); $editCustomerReason = strip_tags(html_entity_decode($this->getRequest()->getData('editCustomerReason'))); $amount = (int) $this->getRequest()->getData('amount'); + $sendEmailToCustomers = (bool) $this->getRequest()->getData('sendEmailToCustomers'); $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); $oldOrderDetail = $this->OrderDetail->find('all', [ @@ -997,7 +997,7 @@ public function editCustomer() $message .= ' '.__d('admin', 'Reason').': "' . $editCustomerReason . '"'; - if (Configure::read('app.sendEmailWhenOrderDetailCustomerChanged')) { + if ($sendEmailToCustomers) { $recipients = [ [ 'email' => $newCustomer->email, @@ -1050,7 +1050,6 @@ public function editProductQuantity() $orderDetailId = (int) $this->getRequest()->getData('orderDetailId'); $productQuantity = trim($this->getRequest()->getData('productQuantity')); - $doNotChangePrice = $this->getRequest()->getData('doNotChangePrice'); $productQuantity = Configure::read('app.numberHelper')->parseFloatRespectingLocale($productQuantity); if (! is_numeric($orderDetailId) || !$productQuantity || $productQuantity < 0) { @@ -1083,30 +1082,28 @@ public function editProductQuantity() $object = clone $oldOrderDetail; // $oldOrderDetail would be changed if passed to function $objectOrderDetailUnit = clone $oldOrderDetail->order_detail_unit; - if (!$doNotChangePrice) { - $newProductPrice = round($oldOrderDetail->order_detail_unit->price_incl_per_unit / $oldOrderDetail->order_detail_unit->unit_amount * $productQuantity, 2); - if ($oldOrderDetail->order_detail_unit->product_quantity_in_units > 0) { - $toleranceFactor = 100; - $oldToNewQuantityRelation = $productQuantity / $oldOrderDetail->order_detail_unit->product_quantity_in_units; - if ($oldToNewQuantityRelation < 1 / $toleranceFactor || $oldToNewQuantityRelation > $toleranceFactor) { - $message = __d('admin', 'The_new_price_would_be_{0}_for_{1}_please_check_the_unit.', [ - '' . Configure::read('app.numberHelper')->formatAsCurrency($newProductPrice) . '', - '' . Configure::read('app.numberHelper')->formatUnitAsDecimal($productQuantity) . ' ' . $oldOrderDetail->order_detail_unit->unit_name . '', - ]); - $this->set([ - 'status' => 0, - 'msg' => $message, - ]); - $this->viewBuilder()->setOption('serialize', ['status', 'msg']); - return; - } - } - if (!empty($oldOrderDetail->order_detail_purchase_price)) { - $productPurchasePrice = round($oldOrderDetail->order_detail_unit->purchase_price_incl_per_unit / $oldOrderDetail->order_detail_unit->unit_amount * $productQuantity, 2); - $this->changeOrderDetailPurchasePrice($oldOrderDetail->order_detail_purchase_price, $productPurchasePrice, $object->product_amount); + $newProductPrice = round((float) $oldOrderDetail->order_detail_unit->price_incl_per_unit / $oldOrderDetail->order_detail_unit->unit_amount * $productQuantity, 2); + if ($oldOrderDetail->order_detail_unit->product_quantity_in_units > 0) { + $toleranceFactor = 100; + $oldToNewQuantityRelation = $productQuantity / $oldOrderDetail->order_detail_unit->product_quantity_in_units; + if ($oldToNewQuantityRelation < 1 / $toleranceFactor || $oldToNewQuantityRelation > $toleranceFactor) { + $message = __d('admin', 'The_new_price_would_be_{0}_for_{1}_please_check_the_unit.', [ + '' . Configure::read('app.numberHelper')->formatAsCurrency($newProductPrice) . '', + '' . Configure::read('app.numberHelper')->formatUnitAsDecimal($productQuantity) . ' ' . $oldOrderDetail->order_detail_unit->unit_name . '', + ]); + $this->set([ + 'status' => 0, + 'msg' => $message, + ]); + $this->viewBuilder()->setOption('serialize', ['status', 'msg']); + return; } - $newOrderDetail = $this->changeOrderDetailPriceDepositTax($object, $newProductPrice, $object->product_amount); } + if (!empty($oldOrderDetail->order_detail_purchase_price)) { + $productPurchasePrice = round((float) $oldOrderDetail->order_detail_unit->purchase_price_incl_per_unit / $oldOrderDetail->order_detail_unit->unit_amount * $productQuantity, 2); + $this->changeOrderDetailPurchasePrice($oldOrderDetail->order_detail_purchase_price, $productPurchasePrice, $object->product_amount); + } + $newOrderDetail = $this->changeOrderDetailPriceDepositTax($object, $newProductPrice, $object->product_amount); $this->changeOrderDetailQuantity($objectOrderDetailUnit, $productQuantity); $message = __d('admin', 'The_weight_of_the_ordered_product_{0}_(amount_{1})_was_successfully_apapted_from_{2}_to_{3}.', [ @@ -1119,7 +1116,7 @@ public function editProductQuantity() $quantityWasChanged = $oldOrderDetail->order_detail_unit->product_quantity_in_units != $productQuantity; // send email to customer if price was changed - if (!$doNotChangePrice && $quantityWasChanged && Configure::read('app.sendEmailWhenOrderDetailQuantityChanged')) { + if ($quantityWasChanged && Configure::read('app.sendEmailWhenOrderDetailQuantityChanged')) { $email = new AppMailer(); $email->viewBuilder()->setTemplate('Admin.order_detail_quantity_changed'); $email->setTo($oldOrderDetail->customer->email) @@ -1131,6 +1128,7 @@ public function editProductQuantity() 'newOrderDetail' => $newOrderDetail, 'appAuth' => $this->AppAuth ]); + $email->addToQueue(); $emailMessage = ' ' . __d('admin', 'An_email_was_sent_to_{0}.', ['' . $oldOrderDetail->customer->name . '']); @@ -1142,11 +1140,15 @@ public function editProductQuantity() '' . $oldOrderDetail->customer->name . '', '' . $oldOrderDetail->product->manufacturer->name . '' ]); - $email->addCC($oldOrderDetail->product->manufacturer->address_manufacturer->email); + $email->setTo($oldOrderDetail->product->manufacturer->address_manufacturer->email); + $orderDetailForManufacturerEmail = $oldOrderDetail; + $orderDetailForManufacturerEmail->customer = $oldOrderDetail->product->manufacturer->address_manufacturer; + $email->setViewVars([ + 'oldOrderDetail' => $orderDetailForManufacturerEmail, + ]); + $email->addToQueue(); } - $email->addToQueue(); - $message .= $emailMessage; } @@ -1237,6 +1239,7 @@ public function editProductAmount() 'appAuth' => $this->AppAuth, 'editAmountReason' => $editAmountReason ]); + $email->addToQueue(); $emailMessage = ' ' . __d('admin', 'An_email_was_sent_to_{0}.', ['' . $oldOrderDetail->customer->name . '']); @@ -1248,11 +1251,15 @@ public function editProductAmount() '' . $oldOrderDetail->customer->name . '', '' . $oldOrderDetail->product->manufacturer->name . '' ]); - $email->addCC($oldOrderDetail->product->manufacturer->address_manufacturer->email); + $orderDetailForManufacturerEmail = $oldOrderDetail; + $orderDetailForManufacturerEmail->customer = $oldOrderDetail->product->manufacturer->address_manufacturer; + $email->setViewVars([ + 'oldOrderDetail' => $orderDetailForManufacturerEmail, + ]); + $email->setTo($oldOrderDetail->product->manufacturer->address_manufacturer->email); + $email->addToQueue(); } - $email->addToQueue(); - $message .= $emailMessage; if ($editAmountReason != '') { @@ -1393,12 +1400,14 @@ public function editProductPrice() $this->Manufacturer = $this->getTableLocator()->get('Manufacturers'); $sendOrderedProductPriceChangedNotification = $this->Manufacturer->getOptionSendOrderedProductPriceChangedNotification($oldOrderDetail->product->manufacturer->send_ordered_product_price_changed_notification); if (! $this->AppAuth->isManufacturer() && $oldOrderDetail->total_price_tax_incl > 0.00 && $sendOrderedProductPriceChangedNotification) { + $orderDetailForManufacturerEmail = $oldOrderDetail; + $orderDetailForManufacturerEmail->customer = $oldOrderDetail->product->manufacturer->address_manufacturer; $email = new AppMailer(); $email->viewBuilder()->setTemplate('Admin.order_detail_price_changed'); $email->setTo($oldOrderDetail->product->manufacturer->address_manufacturer->email) ->setSubject(__d('admin', 'Ordered_price_adapted') . ': ' . $oldOrderDetail->product_name) ->setViewVars([ - 'oldOrderDetail' => $oldOrderDetail, + 'oldOrderDetail' => $orderDetailForManufacturerEmail, 'newOrderDetail' => $newOrderDetail, 'appAuth' => $this->AppAuth, 'editPriceReason' => $editPriceReason, @@ -1599,6 +1608,7 @@ public function addFeedback() 'appAuth' => $this->AppAuth, 'orderDetailFeedback' => $orderDetailFeedback, ]); + $email->customerAnonymizationForManufacturers = false; $email->addToQueue(); @@ -1805,6 +1815,7 @@ public function delete() 'appAuth' => $this->AppAuth, 'cancellationReason' => $cancellationReason ]); + $email->addToQueue(); $emailMessage = ' ' . __d('admin', 'An_email_was_sent_to_{0}.', ['' . $orderDetail->customer->name . '']); @@ -1816,11 +1827,15 @@ public function delete() '' . $orderDetail->customer->name . '', '' . $orderDetail->product->manufacturer->name . '' ]); - $email->addCC($orderDetail->product->manufacturer->address_manufacturer->email); + $email->setTo($orderDetail->product->manufacturer->address_manufacturer->email); + $orderDetailForManufacturerEmail = $orderDetail; + $orderDetailForManufacturerEmail->customer = $orderDetail->product->manufacturer->address_manufacturer; + $email->setViewVars([ + 'orderDetail' => $orderDetailForManufacturerEmail, + ]); + $email->addToQueue(); } - $email->addToQueue(); - $message .= $emailMessage; if ($cancellationReason != '') { @@ -1965,7 +1980,7 @@ public function setElFinderUploadPath($orderDetailId) $this->RequestHandler->renderAs($this, 'json'); $_SESSION['ELFINDER'] = [ - 'uploadUrl' => Configure::read('app.cakeServerName') . "/files/kcfinder/order_details/" . $orderDetailId, + 'uploadUrl' => Configure::read('App.fullBaseUrl') . "/files/kcfinder/order_details/" . $orderDetailId, 'uploadPath' => $_SERVER['DOCUMENT_ROOT'] . "/files/kcfinder/order_details/" . $orderDetailId ]; diff --git a/plugins/Admin/src/Controller/PagesController.php b/plugins/Admin/src/Controller/PagesController.php index dce21c99a5..ddb53ee2ae 100644 --- a/plugins/Admin/src/Controller/PagesController.php +++ b/plugins/Admin/src/Controller/PagesController.php @@ -1,4 +1,5 @@ getRequest()->getParam('action')) { - case 'home': - if ($this->AppAuth->user()) { - return true; - } - break; - default: - return $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(); - } + return match($this->getRequest()->getParam('action')) { + 'home' => $this->AppAuth->user(), + default => $this->AppAuth->isSuperadmin() || $this->AppAuth->isAdmin(), + }; } public function home() @@ -99,7 +95,7 @@ public function edit($pageId) private function _processForm($page, $isEditMode) { $_SESSION['ELFINDER'] = [ - 'uploadUrl' => Configure::read('app.cakeServerName') . "/files/kcfinder/pages", + 'uploadUrl' => Configure::read('App.fullBaseUrl') . "/files/kcfinder/pages", 'uploadPath' => $_SERVER['DOCUMENT_ROOT'] . "/files/kcfinder/pages" ]; $this->set('pagesForSelect', $this->Page->getForSelect($page->id_page)); diff --git a/plugins/Admin/src/Controller/PaymentsController.php b/plugins/Admin/src/Controller/PaymentsController.php index 0b1d963e2b..489be665b7 100644 --- a/plugins/Admin/src/Controller/PaymentsController.php +++ b/plugins/Admin/src/Controller/PaymentsController.php @@ -1,4 +1,5 @@ getRequest()->getParam('action')) { - case 'overview': - return Configure::read('app.htmlHelper')->paymentIsCashless() && $this->AppAuth->user() && ! $this->AppAuth->isManufacturer(); - break; - case 'product': - return $this->AppAuth->isSuperadmin(); - break; - case 'edit': - case 'previewEmail': - return $this->AppAuth->isSuperadmin(); - break; - case 'add': - case 'changeState': - return $this->AppAuth->user(); - break; - default: - return $this->AppAuth->user() && ! $this->AppAuth->isManufacturer(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'overview' => Configure::read('app.htmlHelper')->paymentIsCashless() && $this->AppAuth->user() && ! $this->AppAuth->isManufacturer(), + 'product' => $this->AppAuth->isSuperadmin(), + 'edit', 'previewEmail' => $this->AppAuth->isSuperadmin(), + 'add', 'changeState' => $this->AppAuth->user(), + default => $this->AppAuth->user() && ! $this->AppAuth->isManufacturer(), + }; } public function beforeFilter(EventInterface $event) @@ -144,17 +133,11 @@ public function edit($paymentId) $payment = $this->Payment->save($payment); $this->ActionLog = $this->getTableLocator()->get('ActionLogs'); - switch ($payment->approval) { - case -1: - $actionLogType = 'payment_product_approval_not_ok'; - break; - case 0: - $actionLogType = 'payment_product_approval_open'; - break; - case 1: - $actionLogType = 'payment_product_approval_ok'; - break; - } + $actionLogType = match($payment->approval) { + -1 => 'payment_product_approval_not_ok', + 0 => 'payment_product_approval_open', + 1 => 'payment_product_approval_ok', + }; $newStatusAsString = Configure::read('app.htmlHelper')->getApprovalStates()[$payment->approval]; @@ -292,6 +275,16 @@ public function add() } if ($type == 'deposit') { + if (!isset($userType)) { + $msg = 'no userType set - payment cannot be saved'; + $this->log($msg); + $this->set([ + 'status' => 0, + 'msg' => $msg, + ]); + $this->viewBuilder()->setOption('serialize', ['status', 'msg']); + return; + } $actionLogType .= '_'.$userType; } } @@ -378,14 +371,10 @@ public function add() if (in_array($actionLogType, ['deposit_customer', 'deposit_manufacturer'])) { $message .= '. '; - switch ($actionLogType) { - case 'deposit_customer': - $message .= __d('admin', 'The_amount_was_added_to_the_credit_system_of_{0}_and_can_be_deleted_there.', [''.$customer->name.'']); - break; - case 'deposit_manufacturer': - $message .= __d('admin', 'The_amount_was_added_to_the_deposit_account_of_{0}_and_can_be_deleted_there.', [''.$manufacturer->name.'']); - break; - } + $message .= match($actionLogType) { + 'deposit_customer' => __d('admin', 'The_amount_was_added_to_the_credit_system_of_{0}_and_can_be_deleted_there.', [''.$customer->name.'']), + 'deposit_manufacturer' => __d('admin', 'The_amount_was_added_to_the_deposit_account_of_{0}_and_can_be_deleted_there.', [''.$manufacturer->name.'']), + }; } $this->Flash->success($message); @@ -582,8 +571,10 @@ private function preparePayments() if (! empty($orderDetailsGroupedByMonth)) { foreach ($orderDetailsGroupedByMonth as $orderDetail) { $monthAndYear = explode('-', $orderDetail['MonthAndYear']); + $monthAndYear[0] = (int) $monthAndYear[0]; + $monthAndYear[1] = (int) $monthAndYear[1]; $frozenDateFrom = FrozenDate::create($monthAndYear[0], $monthAndYear[1], 1); - $lastDayOfMonth = Configure::read('app.timeHelper')->getLastDayOfGivenMonth($orderDetail['MonthAndYear']); + $lastDayOfMonth = (int) Configure::read('app.timeHelper')->getLastDayOfGivenMonth($orderDetail['MonthAndYear']); $frozenDateTo = FrozenDate::create($monthAndYear[0], $monthAndYear[1], $lastDayOfMonth); $payments[] = [ 'dateRaw' => $frozenDateFrom, diff --git a/plugins/Admin/src/Controller/ProductsController.php b/plugins/Admin/src/Controller/ProductsController.php index c5bf2bd013..118095c4eb 100644 --- a/plugins/Admin/src/Controller/ProductsController.php +++ b/plugins/Admin/src/Controller/ProductsController.php @@ -1,4 +1,5 @@ Product->find('all', [ - 'conditions' => [ - 'Products.active' => APP_ON, - ], 'contain' => [ 'Manufacturers', 'Images', ], 'order' => [ - 'Products.modified' => 'DESC', + 'Products.active' => 'DESC', 'Images.id_image' => 'ASC', ], ]); @@ -455,6 +454,8 @@ public function saveUploadedImageProduct() ); } else { $image = $product->image; + // cache needs to be cleared manually because neither image nor product record is changed + $this->Product->clearProductCache(); } // not (yet) implemented for attributes, only for productIds! @@ -532,7 +533,7 @@ public function editProductAttribute() '' . $oldProduct->name . '', '' . $oldProduct->manufacturer->name . '', ]); - $this->ActionLog->customSave('product_attribute_deleted', $this->AppAuth->getUserId(), $productAttributeId, 'products', $actionLogMessage); + $this->ActionLog->customSave('product_attribute_deleted', $this->AppAuth->getUserId(), $productId, 'products', $actionLogMessage); $this->getRequest()->getSession()->write('highlightedRowId', $productId); } else { try { @@ -558,7 +559,7 @@ public function editProductAttribute() '' . $oldProduct->name . '', '' . $oldProduct->manufacturer->name . '', ]); - $this->ActionLog->customSave('product_attribute_changed', $this->AppAuth->getUserId(), $productAttributeId, 'products', $actionLogMessage); + $this->ActionLog->customSave('product_attribute_changed', $this->AppAuth->getUserId(), $productId, 'products', $actionLogMessage); $this->getRequest()->getSession()->write('highlightedRowId', $productId . '-' . $productAttributeId); } $this->Flash->success($actionLogMessage); @@ -715,7 +716,7 @@ public function editDeliveryRhythm() $product2update = [ 'delivery_rhythm_count' => $deliveryRhythmCount, - 'delivery_rhythm_type' => $deliveryRhythmType + 'delivery_rhythm_type' => $deliveryRhythmType, ]; $isFirstDeliveryDayMandatory = in_array($deliveryRhythmTypeCombined, ['0-individual', '2-week', '4-week']); @@ -729,7 +730,7 @@ public function editDeliveryRhythm() $product2update['delivery_rhythm_order_possible_until'] = ''; $product2update['delivery_rhythm_send_order_list_day'] = ''; if ($deliveryRhythmSendOrderListWeekday == '') { - $deliveryRhythmSendOrderListWeekday = Configure::read('app.timeHelper')->getNthWeekdayBeforeWeekday(1, Configure::read('app.timeHelper')->getSendOrderListsWeekday()); + $deliveryRhythmSendOrderListWeekday = Configure::read('app.timeHelper')->getNthWeekdayBeforeWeekday(1, DeliveryRhythm::getSendOrderListsWeekday()); } $product2update['delivery_rhythm_send_order_list_weekday'] = Configure::read('app.timeHelper')->getNthWeekdayAfterWeekday(1, $deliveryRhythmSendOrderListWeekday); @@ -765,7 +766,7 @@ public function editDeliveryRhythm() $additionalMessages[] = __d('admin', 'Order_list_is_not_sent'); } } else { - if ($product2update['delivery_rhythm_send_order_list_weekday'] != Configure::read('app.timeHelper')->getSendOrderListsWeekday()) { + if ($product2update['delivery_rhythm_send_order_list_weekday'] != DeliveryRhythm::getSendOrderListsWeekday()) { $additionalMessages[] = __d('admin', 'Last_order_weekday') . ': ' . Configure::read('app.timeHelper')->getWeekdayName( $deliveryRhythmSendOrderListWeekday) . ' ' . __d('admin', 'midnight') . ''; @@ -1728,6 +1729,53 @@ public function changeNewStatus($productId, $status) $this->redirect($this->referer()); } + public function changeStatusBulk() + { + + $this->RequestHandler->renderAs($this, 'json'); + + $this->loadComponent('Sanitize'); + $this->setRequest($this->getRequest()->withParsedBody($this->Sanitize->trimRecursive($this->getRequest()->getData()))); + $this->setRequest($this->getRequest()->withParsedBody($this->Sanitize->stripTagsAndPurifyRecursive($this->getRequest()->getData()))); + + $productIds = $this->request->getData('productIds'); + $status = (int) $this->request->getData('status'); + + $data = []; + foreach($productIds as $productId) { + $productId = (int) $productId; + $data[] = [$productId => $status]; + } + + try { + + $this->Product->changeStatus($data); + $actionLogMessage = __d('admin', '{0,plural,=1{1_product_was} other{#_products_were}}_deactivated.', [ + count($productIds), + ]); + $actionLogType = 'product_set_inactive'; + if ($status) { + $actionLogMessage = __d('admin', '{0,plural,=1{1_product_was} other{#_products_were}}_activated.', [ + count($productIds), + ]); + $actionLogType = 'product_set_active'; + } + $this->Flash->success($actionLogMessage); + $this->ActionLog->customSave($actionLogType, $this->AppAuth->getUserId(), 0, 'products', $actionLogMessage . '
Ids: ' . join(',', $productIds)); + + $this->set([ + 'status' => 1, + 'msg' => __d('admin', 'Saving_successful.'), + ]); + + $this->viewBuilder()->setOption('serialize', ['status', 'msg']); + + } catch (InvalidParameterException $e) { + return $this->sendAjaxError($e); + } + + } + public function changeStatus($productId, $status) { diff --git a/plugins/Admin/src/Controller/ReportsController.php b/plugins/Admin/src/Controller/ReportsController.php index c9b42352d2..5fe1978033 100644 --- a/plugins/Admin/src/Controller/ReportsController.php +++ b/plugins/Admin/src/Controller/ReportsController.php @@ -1,4 +1,5 @@ getRequest()->getParam('pass')[0])) { - switch ($this->getRequest()->getParam('pass')[0]) { + if($this->getRequest()->getParam('pass')[0] == 'deposit') { // allow deposit for cash configuration - case 'deposit': - return $this->AppAuth->isSuperadmin(); - break; + return $this->AppAuth->isSuperadmin(); } } return $this->AppAuth->isSuperadmin() && Configure::read('app.htmlHelper')->paymentIsCashless(); diff --git a/plugins/Admin/src/Controller/SlidersController.php b/plugins/Admin/src/Controller/SlidersController.php index c063c4fc3c..4a94ef7dd3 100644 --- a/plugins/Admin/src/Controller/SlidersController.php +++ b/plugins/Admin/src/Controller/SlidersController.php @@ -1,4 +1,5 @@ getRequest()->getParam('action')) { - case 'index': - return $this->AppAuth->isSuperadmin() || ($this->AppAuth->isAdmin() && Configure::read('app.showStatisticsForAdmins')); - break; - case 'myIndex': - return !Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && $this->AppAuth->isManufacturer(); - break; - default: - return $this->AppAuth->isManufacturer(); - break; - } + return match($this->getRequest()->getParam('action')) { + 'index' => $this->AppAuth->isSuperadmin() || ($this->AppAuth->isAdmin() && Configure::read('app.showStatisticsForAdmins')), + 'myIndex' => !Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && $this->AppAuth->isManufacturer(), + default => $this->AppAuth->isManufacturer(), + }; } /** @@ -61,13 +59,25 @@ public function myIndex() public function index() { - $manufacturerId = $this->getManufacturerId(); + $manufacturerId = (string) $this->getManufacturerId(); + + $range = ''; + if (in_array('range', array_keys($this->getRequest()->getQueryParams()))) { + $range = h($this->getRequest()->getQuery('range')); + } + $this->set('range', $range); - $year = ''; - if (!empty($this->getRequest()->getQuery('year'))) { - $year = h($this->getRequest()->getQuery('year')); + $year = null; + $lastMonths = null; + if (preg_match('`year-`', $range)) { + $year = preg_replace('`year-`', '', $range); + } + if (preg_match('`last-months-`', $range)) { + $lastMonths = preg_replace('`last-months-`', '', $range); + if (!in_array($lastMonths, [12,24])) { + throw new InvalidParameterException($lastMonths . ' not valid as last-months parameter'); + } } - $this->set('year', $year); $this->Manufacturer = $this->getTableLocator()->get('Manufacturers'); $manufacturersForDropdown = []; @@ -102,7 +112,22 @@ public function index() } $this->set('title_for_layout', $titleForLayout); - $this->set('years', Configure::read('app.timeHelper')->getAllYearsUntilThisYear(date('Y'), 2014)); + $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); + $firstOrderYear = $this->OrderDetail->getFirstOrderYear($manufacturerId); + $lastOrderYear = $this->OrderDetail->getLastOrderYear($manufacturerId); + + $rangesForDropdown = [ + '' => __d('admin', 'Total'), + 'last-months-12' => __d('admin', 'Last_{0}_months', [12]), + 'last-months-24' => __d('admin', 'Last_{0}_months', [24]), + ]; + if ($lastOrderYear !== false && $firstOrderYear !== false) { + $allYears = Configure::read('app.timeHelper')->getAllYearsUntilThisYear($lastOrderYear, $firstOrderYear); + foreach($allYears as $y) { + $rangesForDropdown['year-' . $y] = $y; + } + } + $this->set('ranges', $rangesForDropdown); $excludeMemberFeeCondition = []; if (Configure::read('appDb.FCS_MEMBER_FEE_PRODUCTS') != '') { @@ -111,8 +136,14 @@ public function index() ]; } - $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); - $monthlySumProducts = $this->OrderDetail->getMonthlySumProductByManufacturer($manufacturerId, $year); + if ($lastMonths !== null) { + $monthlySumProducts = $this->OrderDetail->getMonthlySumProductByManufacturer($manufacturerId, ''); + $firstDayOfLastOrderMonth = $this->OrderDetail->getFirstDayOfLastOrderMonth($manufacturerId); + $monthlySumProducts = $this->OrderDetail->addLastMonthsCondition($monthlySumProducts, $firstDayOfLastOrderMonth, $lastMonths); + } else { + $monthlySumProducts = $this->OrderDetail->getMonthlySumProductByManufacturer($manufacturerId, $year); + } + if (!empty($excludeMemberFeeCondition)) { $monthlySumProducts->where($excludeMemberFeeCondition); } @@ -128,7 +159,7 @@ public function index() return; } - $monthsAndYear = Configure::read('app.timeHelper')->getAllMonthsUntilThisYear(date('Y'), 2014); + $monthsAndYear = Configure::read('app.timeHelper')->getAllMonthsUntilThisYear($lastOrderYear, $firstOrderYear); $monthsWithTurnoverMonthAndYear = $monthlySumProducts->all()->extract('MonthAndYear')->toArray(); $monthsWithTurnoverSumTotalPaid = $monthlySumProducts->all()->extract('SumTotalPaid')->toArray(); @@ -251,7 +282,13 @@ public function index() $data = []; foreach($manufacturers as $manufacturer) { - $monthlySumProductsQuery = $this->OrderDetail->getMonthlySumProductByManufacturer($manufacturer->id_manufacturer, $year); + if ($lastMonths !== null) { + $monthlySumProductsQuery = $this->OrderDetail->getMonthlySumProductByManufacturer($manufacturer->id_manufacturer, $year); + $monthlySumProductsQuery = $this->OrderDetail->addLastMonthsCondition($monthlySumProductsQuery, $firstDayOfLastOrderMonth, $lastMonths); + } else { + $monthlySumProductsQuery = $this->OrderDetail->getMonthlySumProductByManufacturer($manufacturer->id_manufacturer, $year); + } + if (!empty($excludeMemberFeeCondition)) { $monthlySumProductsQuery->where($excludeMemberFeeCondition); } diff --git a/plugins/Admin/src/Controller/TaxesController.php b/plugins/Admin/src/Controller/TaxesController.php index aef4000b67..1ef831087c 100644 --- a/plugins/Admin/src/Controller/TaxesController.php +++ b/plugins/Admin/src/Controller/TaxesController.php @@ -1,4 +1,5 @@ Form->create(null, ['type' => 'get']); ?> isManufacturer() || $appAuth->isSuperadmin() || $appAuth->isAdmin()) { ?> Form->control('types', ['type' => 'select', 'multiple' => true, 'empty' => __d('admin', 'all_activities'), 'label' => '', 'options' => $actionLogModel->getTypesForDropdown($appAuth), 'data-val' => join(',', $types)]); ?> + + isSuperadmin() || $appAuth->isAdmin()) { ?> Form->control('customerId', ['type' => 'select', 'label' => '', 'placeholder' => __d('admin', 'all_users'), 'options' => []]); ?> + + isManufacturer() || $appAuth->isSuperadmin() || $appAuth->isAdmin()) { ?> Form->control('productId', ['type' => 'select', 'label' => '', 'placeholder' => __d('admin', 'all_products'), 'options' => []]); ?> element('dateFields', ['dateFrom' => $dateFrom, 'dateTo' => $dateTo, 'nameFrom' => 'dateFrom', 'nameTo' => 'dateTo']); ?> @@ -82,16 +88,19 @@ echo ''; echo ''; - echo $actionLog->date->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateNTimeLongWithSecs')); + echo $actionLog->date->i18nFormat($this->Time->getI18Format('DateNTimeLongWithSecs')); echo ''; - echo ''; + echo ''; echo $actionLog->text; echo ''; echo ''; if ($actionLog->customer) { $name = $actionLog->customer->name; + if ($appAuth->isManufacturer() && $appAuth->getManufacturerAnonymizeCustomers()) { + $name = $this->Html->anonymizeCustomerName($name, $actionLog->customer->id_customer); + } if ($actionLog->customer->manufacturer) { $name = $actionLog->customer->manufacturer->name; } diff --git a/plugins/Admin/templates/Attributes/edit.php b/plugins/Admin/templates/Attributes/edit.php index c6a591ffe7..a2462a57c6 100644 --- a/plugins/Admin/templates/Attributes/edit.php +++ b/plugins/Admin/templates/Attributes/edit.php @@ -1,4 +1,6 @@ Form->create(null, ['type' => 'get']); ?>

- Form->control('customerId', ['type' => 'select', 'label' => '', 'placeholder' => __d('admin', 'all_members'), 'options' => []]); ?> + isSuperadmin() || $appAuth->isAdmin()) { ?> + Form->control('customerId', ['type' => 'select', 'label' => '', 'placeholder' => __d('admin', 'all_members'), 'options' => []]); ?> + isSuperadmin() || $appAuth->isAdmin())) { echo $this->Form->control('manufacturerId', [ @@ -93,7 +97,7 @@ echo $blogPost->id_blog_post; echo ''; - echo ''; + echo ''; $srcLargeImage = $this->Html->getBlogPostImageSrc($blogPost, 'single'); $srcSmallImage = $this->Html->getBlogPostImageSrc($blogPost, 'home'); @@ -139,7 +143,11 @@ echo $blogPost->customer->manufacturer->name; } else { if (!empty($blogPost->customer)) { - echo $blogPost->customer->name; + $customerName = $blogPost->customer->name; + if ($appAuth->isManufacturer() && $appAuth->getManufacturerAnonymizeCustomers()) { + $customerName = $this->Html->anonymizeCustomerName($customerName, $blogPost->customer->id_customer); + } + echo $customerName; } } echo ''; diff --git a/plugins/Admin/templates/Categories/edit.php b/plugins/Admin/templates/Categories/edit.php index 48155e39d9..53e9ee4d41 100644 --- a/plugins/Admin/templates/Categories/edit.php +++ b/plugins/Admin/templates/Categories/edit.php @@ -1,4 +1,6 @@ +element('navTabs/configurationNavTabs', [ + 'key' => 'configurations', + ]); +?> + +
'select', 'multiple' => true, 'data-val' => $value, + 'data-live-search' => true, 'label' => $label, 'options' => $this->Configuration->getConfigurationDropdownOptions($configuration->name, $appAuth), 'escape' => false, - 'empty' => $this->Configuration->getConfigurationDropdownEmpty($configuration->name), ]); break; } diff --git a/plugins/Admin/templates/Configurations/index.php b/plugins/Admin/templates/Configurations/index.php index 7b9ddbd0f6..5bcfe6f8cf 100644 --- a/plugins/Admin/templates/Configurations/index.php +++ b/plugins/Admin/templates/Configurations/index.php @@ -1,4 +1,6 @@

- element('headerIcons', ['helperLink' => $this->Html->getDocsUrl(__d('admin', 'docs_route_settings'))]); ?> + element('headerIcons', ['helperLink' => $this->Html->getDocsUrl(__d('admin', 'docs_route_settings'), 'en')]); ?>
+ element('navTabs/configurationNavTabs', [ + 'key' => 'configurations', + ]); + ?> +

@@ -89,23 +98,12 @@ echo ''; @@ -200,19 +198,11 @@ echo ''; echo ''; echo ''; @@ -232,8 +222,8 @@ - - + + @@ -328,11 +318,6 @@ - - - - - @@ -353,18 +338,6 @@ - elementExists('latestGitCommit')) { - echo ''; - echo ''; - echo ''; - echo ''; - } - ?> - @@ -375,33 +348,38 @@ + + + + + - + - + - + - + - + diff --git a/plugins/Admin/templates/Configurations/send_test_email.php b/plugins/Admin/templates/Configurations/send_test_email.php index 20c26b1cdf..2a85b826c2 100644 --- a/plugins/Admin/templates/Configurations/send_test_email.php +++ b/plugins/Admin/templates/Configurations/send_test_email.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +use Cake\Core\Configure; + +$this->element('addScript', [ 'script' => + Configure::read('app.jsNamespace') . ".Admin.init();" . + Configure::read('app.jsNamespace') . ".Admin.initForm();". + Configure::read('app.jsNamespace') . ".Admin.selectMainMenuAdmin('".__d('admin', 'Website_administration')."', '".__d('admin', 'Configurations')."'); + " +]); +?> + +
+

+
+ + element('printIcon'); ?> +
+
+ +element('navTabs/configurationNavTabs', [ + 'key' => 'cronjobs', + ]); +?> +
+ +Form->create($cronjob, [ + 'class' => 'fcs-form', + 'novalidate' => 'novalidate', + 'url' => $this->Slug->getCronjobEdit($cronjob->id), + 'id' => 'cronjobEditForm', +]); + +echo $this->Form->hidden('referer', ['value' => $referer]); + +echo '

' . $cronjob->name . '

'; + +echo $this->Form->control('Cronjobs.time_interval', [ + 'label' => __d('admin', 'Time_interval'), + 'type' => 'select', + 'options' => $timeIntervals, +]); + +echo $this->Form->control('Cronjobs.day_of_month', [ + 'label' => __d('admin', 'Day_of_month'), + 'type' => 'select', + 'empty' => __d('admin', 'Please_select...'), + 'options' => $daysOfMonth, +]); + +echo $this->Form->control('Cronjobs.weekday', [ + 'label' => __d('admin', 'Weekday'), + 'type' => 'select', + 'empty' => __d('admin', 'Please_select...'), + 'options' => $weekdays, +]); + +echo $this->Form->control('Cronjobs.not_before_time', [ + 'label' => __d('admin', 'Not_before_time').' '.__d('admin', 'Cronjob_is_called_up_to_10_min_after_the_given_time.').'', + 'type' => 'time', + 'escape' => false, +]); + +echo $this->Form->control('Cronjobs.active', [ + 'label' => __d('admin', 'Active'), + 'type' => 'checkbox', +]); + + +echo $this->Form->end(); + +?> diff --git a/plugins/Admin/templates/Cronjobs/index.php b/plugins/Admin/templates/Cronjobs/index.php new file mode 100644 index 0000000000..e8f60fc630 --- /dev/null +++ b/plugins/Admin/templates/Cronjobs/index.php @@ -0,0 +1,156 @@ + + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +use Cake\Core\Configure; +use Cake\Utility\Inflector; +use Cake\Datasource\FactoryLocator; + +$this->element('addScript', [ + 'script' => + Configure::read('app.jsNamespace') . ".Admin.init(); " . + Configure::read('app.jsNamespace') . ".Admin.selectMainMenuAdmin('" . __d('admin', 'Website_administration') . "', '" . __d('admin', 'Configurations') . "'); + " +]); + +?> + +
+

+
+ element('printIcon'); + ?> +
+ +
+ +element('navTabs/configurationNavTabs', [ + 'key' => 'cronjobs', +]); + +echo '
'; - switch ($configuration->type) { - case 'number': - case 'text': - case 'textarea': - case 'textarea_big': - echo $configuration->value; - break; - case 'dropdown': - echo $this->Configuration->getConfigurationDropdownOption($configuration->name, $configuration->value, $appAuth); - break; - case 'multiple_dropdown': - echo $this->Configuration->getConfigurationMultipleDropdownOptions($configuration->name, $configuration->value); - break; - case 'boolean': - echo (boolean) $configuration->value ? __d('admin', 'yes') : __d('admin', 'no'); - break; - } + echo match($configuration->type) { + 'number', 'text', 'textarea', 'textarea_big' => $configuration->value, + 'dropdown' => $this->Configuration->getConfigurationDropdownOption($configuration->name, $configuration->value, $appAuth), + 'multiple_dropdown' => $this->Configuration->getConfigurationMultipleDropdownOptions($configuration->name, $configuration->value), + 'boolean' => (bool) $configuration->value ? __d('admin', 'yes') : __d('admin', 'no'), + }; echo ''; - - switch($configuration->name) { - case 'FCS_WEEKLY_PICKUP_DAY': - echo $this->MyTime->getWeekdayName($configuration->value); - break; - case 'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA': - echo $configuration->value . ' (' . $this->MyTime->getWeekdayName($this->MyTime->getSendOrderListsWeekday()) . ')'; - break; - default: - echo $configuration->value; - break; - } - + echo match($configuration->name) { + 'FCS_WEEKLY_PICKUP_DAY' => $this->MyTime->getWeekdayName($configuration->value), + 'FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA' => $configuration->value . ' (' . $this->MyTime->getWeekdayName(DeliveryRhythm::getSendOrderListsWeekday()) . ')', + default => $configuration->value, + }; echo '
app.cakeServerNameApp.fullBaseUrl
app.sendEmailWhenOrderDetailCustomerChanged
app.showTaxSumTableOnOrderDetailPdf
'.__d('admin', 'Software_update_version').''; - echo nl2br($this->element('latestGitCommit')); - echo __d('admin', 'Please_find_more_information_in_the_changelog_{0}.', ['Changelog']); - echo '
app.showTaxInOrderConfirmationEmail
app.paypalMeUsername
app.defaultTax Number->formatAsPercent($defaultTax->rate); ?> - active ? __d('admin', 'activated') : __d('admin', 'deactivated'); ?>
260px
/files/images/
260px
/files/images/
260px
/files/images/logo-pdf.jpg
260px
/files/images/logo-pdf.jpg
150x150
/files/images/products/de-default-home_default.jpg
150x150
/files/images/products/de-default-home_default.jpg
150x150
/files/images/manufacturers/de-default-medium_default.jpg
150x150
/files/images/manufacturers/de-default-medium_default.jpg
150x113
/files/images/blog_posts/no-home-default.jpg
150x113
/files/images/blog_posts/no-home-default.jpg
'; + +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; + +$i = 0; +foreach ($cronjobs as $cronjob) { + $i++; + $rowClass = [ + 'data' + ]; + if (!$cronjob->active) { + $rowClass[] = 'deactivated'; + $rowClass[] = 'line-through'; + } + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; + + echo ''; +} + +echo ''; +echo ''; +echo ''; + +echo '
ID' . __d('admin', 'Name') . '' . __d('admin', 'Time_interval') . '' . __d('admin', 'Day_of_month') . '' . __d('admin', 'Weekday') . '' . __d('admin', 'Not_before_time') . '' . __d('admin', 'Active') . '' . __d('admin', 'Last_run') . '
'; + echo $this->Html->link( + '', + $this->Slug->getCronjobEdit($cronjob->id), + [ + 'class' => 'btn btn-outline-light', + 'title' => __d('admin', 'Edit'), + 'escape' => false + ] + ); + echo ''; + echo $cronjob->id; + echo ''; + echo $cronjob->name; + echo ''; + echo $this->Time->getTranslatedTimeInterval($cronjob->time_interval); + echo ''; + if ($cronjob->day_of_month != '') { + $cm = FactoryLocator::get('Table')->get('Cronjobs'); + echo $cm->getDaysOfMonth()[$cronjob->day_of_month]; + } + echo ''; + if ($cronjob->weekday != '') { + echo $this->Time->getWeekdayName( + $this->Time->formatAsWeekday( + strtotime('next ' . $cronjob->weekday), // trick to get eg. 6 from Saturday + ) + ); + } + echo ''; + echo $cronjob->not_before_time->i18nFormat($this->Time->getI18Format('TimeShort')); + echo ''; + if ($cronjob->active == 1) { + echo ''; + } else { + echo ''; + } + echo ''; + if (!empty($cronjob->cronjob_logs[0])) { + $name = $cronjob->getOriginalValues()['name']; + $cronjobFilterString = Inflector::underscore($name); + if (preg_match('/SendInvoicesToManufacturers/', $name)) { + $cronjobFilterString = 'send_invoices'; + } + $cronjobFilterString = 'cronjob_' . $cronjobFilterString; + if (preg_match('/SendInvoicesToCustomers/', $name)) { + $cronjobFilterString = 'invoice_added'; + } + $cronjobFilterString = $cronjobFilterString; + echo $this->Html->link( + $this->Time->getWeekdayName($this->Time->formatAsWeekday((int) $cronjob->cronjob_logs[0]->created->toUnixString())) . ', ' . + $cronjob->cronjob_logs[0]->created->i18nFormat($this->Time->getI18Format('DateNTimeShort')), + $this->Slug->getActionLogsList() . '/?dateFrom=' . date(Configure::read('app.timeHelper')->getI18Format('DateShortAlt'), strtotime('-3 month')) . '&types[]=' . $cronjobFilterString, + ); + } + echo '
' . $i . ' ' . __d('admin', '{0,plural,=1{record} other{records}}', $i) . '
'; + +?> \ No newline at end of file diff --git a/plugins/Admin/templates/Customers/change_password.php b/plugins/Admin/templates/Customers/change_password.php index fecbc9c416..561510c1c0 100644 --- a/plugins/Admin/templates/Customers/change_password.php +++ b/plugins/Admin/templates/Customers/change_password.php @@ -1,4 +1,6 @@ element('reportNavTabs', [ +echo $this->element('navTabs/reportNavTabs', [ 'key' => 'credit_balance_sum', 'dateFrom' => $dateFrom, 'dateTo' => $dateTo diff --git a/plugins/Admin/templates/Customers/edit.php b/plugins/Admin/templates/Customers/edit.php index 6c5df55cd2..ffad88fb40 100644 --- a/plugins/Admin/templates/Customers/edit.php +++ b/plugins/Admin/templates/Customers/edit.php @@ -1,4 +1,6 @@ ' . $this->Paginator->sort('Customers.id_default_group', __d('admin', 'Group')) . ''; echo '' . $this->Paginator->sort('Customers.email', __d('admin', 'Email')) . ''; echo '' . $this->Paginator->sort('Customers.active', __d('admin', 'Status')) . ''; -echo ''.__d('admin', 'Ordered_products').''; if (Configure::read('app.htmlHelper')->paymentIsCashless()) { - echo ''.__d('admin', 'Credit').''; + echo '' . $this->Paginator->sort('credit_balance', __d('admin', 'Credit'), ['direction' => 'desc']) . ''; } if (Configure::read('app.emailOrderReminderEnabled')) { - echo '' . $this->Paginator->sort('Customers.email_order_reminder_enabled', __d('admin', 'Reminder')) . ''; + echo '' . $this->Paginator->sort('Customers.email_order_reminder_enabled', __d('admin', 'Order_reminder')) . ''; +} +if (Configure::read('app.htmlHelper')->paymentIsCashless()) { + echo '' . $this->Paginator->sort('Customers.check_credit_reminder_enabled', __d('admin', 'Check_credit_reminder')) . ''; } if (Configure::read('appDb.FCS_NEWSLETTER_ENABLED')) { echo '' . $this->Paginator->sort('Customers.newsletter_enabled', __d('admin', 'Newsletter')) . ''; @@ -84,9 +88,9 @@ echo '' . $this->Paginator->sort('Feedbacks.modified', __d('admin', 'Feedback')) . ''; } echo '' . $this->Paginator->sort('Customers.date_add', __d('admin', 'Register_date')) . ''; -echo ''.__d('admin', 'Last_pickup_day').''; +echo '' . $this->Paginator->sort('last_pickup_day', __d('admin', 'Last_pickup_day'), ['direction' => 'desc']) . ''; if (Configure::read('appDb.FCS_MEMBER_FEE_PRODUCTS') != '') { - echo '' . $this->Paginator->sort('Customers.member_fee', __d('admin', 'Member_fee')) . ''; + echo '' . $this->Paginator->sort('member_fee', __d('admin', 'Member_fee')) . ''; } if (Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS')) { echo '' . $this->Paginator->sort('Customers.shopping_price', __d('admin', 'Prices')) . ''; @@ -95,8 +99,8 @@ echo ''; $i = 0; -$sumOrderDetailsCount = 0; -$sumEmailReminders = 0; +$sumOrderReminders = 0; +$sumCreditReminders = 0; $sumNewsletter = 0; $sumFeedback = 0; $sumFeedbackNotApproved = 0; @@ -134,10 +138,15 @@ ]).'"> ' . $customerName; } - echo '' . $this->Html->link($customerName, '/admin/order-details?&pickupDay[]='.Configure::read('app.timeHelper')->formatToDateShort('2014-01-01').'&pickupDay[]=' . Configure::read('app.timeHelper')->formatToDateShort('2022-12-31') . '&customerId=' . $customer->id_customer . '&sort=OrderDetails.pickup_day&direction=desc', [ - 'title' => __d('admin', 'Show_all_orders_from_{0}', [$this->Html->getNameRespectingIsDeleted($customer)]), - 'escape' => false - ]) . ''; + if ($lastOrderYear === false && $firstOrderYear === false) { + $customerLink = $customerName; + } else { + $customerLink = $this->Html->link($customerName, '/admin/order-details?&pickupDay[]='.Configure::read('app.timeHelper')->formatToDateShort($firstOrderYear . '-01-01').'&pickupDay[]=' . Configure::read('app.timeHelper')->formatToDateShort($lastOrderYear . '-12-31') . '&customerId=' . $customer->id_customer . '&sort=OrderDetails.pickup_day&direction=desc', [ + 'title' => __d('admin', 'Show_all_orders_from_{0}', [$this->Html->getNameRespectingIsDeleted($customer)]), + 'escape' => false + ]); + } + echo '' . $customerLink . ''; echo '
'; $imageSrc = $this->Html->getCustomerImageSrc($customer->id_customer, 'small'); @@ -222,11 +231,6 @@ echo ''; - echo ''; - echo $this->Number->formatAsDecimal($customer->order_detail_count, 0); - $sumOrderDetailsCount += $customer->order_detail_count; - echo ''; - if ($this->Html->paymentIsCashless()) { $negativeClass = $customer->credit_balance < 0 ? 'negative' : ''; echo ''; @@ -255,7 +259,16 @@ echo ''; if ($customer->email_order_reminder_enabled) { echo ''; - $sumEmailReminders++; + $sumOrderReminders++; + } + echo ''; + } + + if ($this->Html->paymentIsCashless()) { + echo ''; + if ($customer->check_credit_reminder_enabled) { + echo ''; + $sumCreditReminders++; } echo ''; } @@ -311,8 +324,8 @@ echo ''; echo ''; - if (!empty($customer->last_order_date)) { - echo $customer->last_order_date->pickup_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateShort')); + if (!empty($customer->last_pickup_day)) { + echo $customer->last_pickup_day->pickup_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateShort')); } echo ''; @@ -352,20 +365,24 @@ echo ''; echo '' . $i . ' '.__d('admin', '{0,plural,=1{record} other{records}}', $i).''; -echo '' . $this->Number->formatAsDecimal($sumOrderDetailsCount, 0) . ''; +$colspan = 3; if ($this->Html->paymentIsCashless()) { echo ''; } if (Configure::read('app.emailOrderReminderEnabled')) { - echo '' . $sumEmailReminders . ''; + echo '' . $sumOrderReminders . ''; +} +if ($this->Html->paymentIsCashless()) { + echo '' . $sumCreditReminders . ''; } if (Configure::read('appDb.FCS_NEWSLETTER_ENABLED')) { echo '' . $sumNewsletter . ''; } if (Configure::read('appDb.FCS_USER_FEEDBACK_ENABLED') && $sumFeedback > 0) { echo '' . $sumFeedback . ($sumFeedbackNotApproved > 0 ? ' (' . $sumFeedbackNotApproved . ')' : ''). ''; +} else { + $colspan++; } -$colspan = 3; if (Configure::read('appDb.FCS_MEMBER_FEE_PRODUCTS') != '') { $colspan++; } diff --git a/plugins/Admin/templates/Deposits/detail.php b/plugins/Admin/templates/Deposits/detail.php index 2216ea1098..7c1362b000 100644 --- a/plugins/Admin/templates/Deposits/detail.php +++ b/plugins/Admin/templates/Deposits/detail.php @@ -1,4 +1,6 @@ element('reportNavTabs', [ +echo $this->element('navTabs/reportNavTabs', [ 'key' => 'deposit_overview', 'dateFrom' => $dateFrom, 'dateTo' => $dateTo, diff --git a/plugins/Admin/templates/Feedbacks/form.php b/plugins/Admin/templates/Feedbacks/form.php index 309c6f7394..210e75e514 100644 --- a/plugins/Admin/templates/Feedbacks/form.php +++ b/plugins/Admin/templates/Feedbacks/form.php @@ -1,4 +1,6 @@ element('reportNavTabs', [ + echo $this->element('navTabs/reportNavTabs', [ 'key' => 'invoices', 'dateFrom' => $dateFrom, 'dateTo' => $dateTo, diff --git a/plugins/Admin/templates/Lists/order_lists.php b/plugins/Admin/templates/Lists/order_lists.php index a7adb5e658..43ce63a7ec 100644 --- a/plugins/Admin/templates/Lists/order_lists.php +++ b/plugins/Admin/templates/Lists/order_lists.php @@ -1,4 +1,6 @@ '; echo ''; - echo $this->Html->link( - ' ' . __d('admin', 'Show_list_(grouped_by_product)'), - $file['product_list_link'], - [ - 'class' => 'btn btn-outline-light', - 'target' => '_blank', - 'title' => __d('admin', 'Show_list_(grouped_by_product)'), - 'escape' => false - ] - ); + foreach($file['product_lists'] as $productList) { + echo $this->Html->link( + ' ' . $productList['label'], + $productList['link'], + [ + 'class' => 'btn btn-outline-light', + 'target' => '_blank', + 'escape' => false, + ] + ); + } echo ''; echo ''; - echo $this->Html->link( - ' ' . __d('admin', 'Show_list_(grouped_by_member)'), - $file['customer_list_link'], - [ - 'class' => 'btn btn-outline-light', - 'target' => '_blank', - 'title' => __d('admin', 'Show_list_(grouped_by_member)'), - 'escape' => false - ] - ); + foreach($file['customer_lists'] as $customerList) { + echo $this->Html->link( + ' ' . $customerList['label'], + $customerList['link'], + [ + 'class' => 'btn btn-outline-light', + 'target' => '_blank', + 'escape' => false, + ] + ); + } echo ''; echo ''; diff --git a/plugins/Admin/templates/Manufacturers/edit.php b/plugins/Admin/templates/Manufacturers/edit.php index 75eb6c40f4..b2af55ddec 100644 --- a/plugins/Admin/templates/Manufacturers/edit.php +++ b/plugins/Admin/templates/Manufacturers/edit.php @@ -1,4 +1,6 @@ echo '
'; } + if (!$appAuth->isManufacturer()) { + echo $this->Form->control('Manufacturers.anonymize_customers', [ + 'label' => __d('admin', 'Anonymize_customers?').' '.__d('admin', 'Customer_data_(firstname_lastname_email)_will_either_be_hidden_or_anonymized_for_this_manufacturer.').'', + 'type' => 'checkbox', + 'escape' => false + ]); + } + echo $this->Form->end(); ?> diff --git a/plugins/Admin/templates/Manufacturers/index.php b/plugins/Admin/templates/Manufacturers/index.php index 49455abdc0..e582462267 100644 --- a/plugins/Admin/templates/Manufacturers/index.php +++ b/plugins/Admin/templates/Manufacturers/index.php @@ -1,4 +1,6 @@ element('highlightRowAfterEdit', [ @@ -103,7 +105,7 @@ echo ''; echo $manufacturer->id_manufacturer; echo ''; - echo ''; + echo ''; $srcLargeImage = $this->Html->getManufacturerImageSrc($manufacturer->id_manufacturer, 'large'); $largeImageExists = preg_match('/de-default-large_default/', $srcLargeImage); if (! $largeImageExists) { @@ -236,24 +238,50 @@ } echo ''; - $testOrderListLinks = '' . h($manufacturer->name) . '
'; + $orderListProductBaseLink = '/admin/manufacturers/getOrderListByProduct.pdf?manufacturerId=' . $manufacturer->id_manufacturer . '&pickupDay=' . $dateFrom; + $testOrderListLinks = '
'; + $testOrderListLinks .= '

' . h($manufacturer->name) . '
'; + $testOrderListLinks .= __d('admin', 'Anonymize_customers?') . ' ' . ($manufacturer->anonymize_customers ? __d('admin', 'yes') . ' ' : __d('admin', 'no') . ' ') . '

'; + $testOrderListLinks .= $this->Html->link( + ' ' . __d('admin', 'Order_list_with_clear_names') . ' - ' . __d('admin', 'grouped_by_product'), + $orderListProductBaseLink . '&isAnonymized=0', + [ + 'class' => 'btn btn-outline-light', + 'style' => 'margin-bottom:5px;', + 'target' => '_blank', + 'escape' => false, + ]); + $testOrderListLinks .= '
'; + $testOrderListLinks .= $this->Html->link( + ' ' . __d('admin', 'Anonymized_list') . ' - ' . __d('admin', 'grouped_by_product'), + $orderListProductBaseLink . '&isAnonymized=1', + [ + 'class' => 'btn btn-outline-light', + 'style' => 'margin-bottom:15px;', + 'target' => '_blank', + 'escape' => false, + ]); + $testOrderListLinks .= '
'; + $orderListCustomerBaseLink = '/admin/manufacturers/getOrderListByCustomer.pdf?manufacturerId=' . $manufacturer->id_manufacturer . '&pickupDay=' . $dateFrom; $testOrderListLinks .= $this->Html->link( - __d('admin', 'Order_list_by_product'), - '/admin/manufacturers/getOrderListByProduct.pdf?manufacturerId=' . $manufacturer->id_manufacturer . '&pickupDay=' . $dateFrom, + ' ' . __d('admin', 'Order_list_with_clear_names') . ' - ' . __d('admin', 'grouped_by_customer'), + $orderListCustomerBaseLink . '&isAnonymized=0', [ 'class' => 'btn btn-outline-light', - 'style' => 'margin-bottom:5px;text-decoration:none ! important;', + 'style' => 'margin-bottom:5px;', 'target' => '_blank', + 'escape' => false, ]); $testOrderListLinks .= '
'; $testOrderListLinks .= $this->Html->link( - __d('admin', 'Order_list_by_member'), - '/admin/manufacturers/getOrderListByCustomer.pdf?manufacturerId=' . $manufacturer->id_manufacturer . '&pickupDay=' . $dateFrom, + ' ' . __d('admin', 'Anonymized_order_list') . ' - ' . __d('admin', 'grouped_by_customer'), + $orderListCustomerBaseLink . '&isAnonymized=1', [ 'class' => 'btn btn-outline-light', - 'style' => 'text-decoration:none ! important;', 'target' => '_blank', + 'escape' => false, ]); + $testOrderListLinks .= '
'; echo '' . __d('admin', 'Test_order_list').''; echo ''; diff --git a/plugins/Admin/templates/OrderDetails/edit_purchase_price.php b/plugins/Admin/templates/OrderDetails/edit_purchase_price.php index 8aa0721f20..8a585d86ca 100644 --- a/plugins/Admin/templates/OrderDetails/edit_purchase_price.php +++ b/plugins/Admin/templates/OrderDetails/edit_purchase_price.php @@ -1,4 +1,6 @@ Configure::read('app.jsNamespace') . ".Helper.initDatepicker(); $('input.datepicker').datepicker();". Configure::read('app.jsNamespace').".Admin.init();" . - Configure::read('app.jsNamespace').".Helper.setCakeServerName('" . Configure::read('app.cakeServerName') . "');" . + Configure::read('app.jsNamespace').".Helper.setFullBaseUrl('" . Configure::read('App.fullBaseUrl') . "');" . Configure::read('app.jsNamespace').".Helper.setIsManufacturer(" . $appAuth->isManufacturer() . ");" . Configure::read('app.jsNamespace').".Admin.selectMainMenuAdmin('".__d('admin', 'Orders')."');" . Configure::read('app.jsNamespace').".Admin.initProductDropdown(" . ($productId != '' ? $productId : '0') . ", " . ($manufacturerId != '' ? $manufacturerId : '0') . ");". diff --git a/plugins/Admin/templates/OrderDetails/profit.php b/plugins/Admin/templates/OrderDetails/profit.php index d0a38515cb..4d3310157c 100644 --- a/plugins/Admin/templates/OrderDetails/profit.php +++ b/plugins/Admin/templates/OrderDetails/profit.php @@ -1,4 +1,6 @@ element('reportNavTabs', [ +echo $this->element('navTabs/reportNavTabs', [ 'key' => 'profit', 'dateFrom' => $dateFrom, 'dateTo' => $dateTo, diff --git a/plugins/Admin/templates/Pages/edit.php b/plugins/Admin/templates/Pages/edit.php index 8158eb6a78..99fc8c5c1f 100644 --- a/plugins/Admin/templates/Pages/edit.php +++ b/plugins/Admin/templates/Pages/edit.php @@ -1,4 +1,6 @@ element('addScript', [ 'script' => + Configure::read('app.jsNamespace') . ".ColorMode.init();" . Configure::read('app.jsNamespace') . ".Helper.showContent();" . Configure::read('app.jsNamespace') . ".Helper.initAnystretch();" . Configure::read('app.jsNamespace') . ".Admin.setMenuFixed();" . diff --git a/plugins/Admin/templates/Pages/index.php b/plugins/Admin/templates/Pages/index.php index 9f863ffbfc..48efaf4893 100644 --- a/plugins/Admin/templates/Pages/index.php +++ b/plugins/Admin/templates/Pages/index.php @@ -1,4 +1,6 @@ '; if ($payment['type'] == 'product') { - switch ($payment['approval']) { - case APP_DEL: - echo ''; - break; - case APP_OFF: - break; - case APP_ON: - echo ''; - break; - } + echo match($payment['approval']) { + APP_DEL => '', + APP_OFF => '', + APP_ON => '', + }; if ($payment['approval_comment'] != '') { echo $this->Html->link( '', diff --git a/plugins/Admin/templates/Products/detect_missing_product_images.php b/plugins/Admin/templates/Products/detect_missing_product_images.php index e12c5229f4..648c1182e3 100644 --- a/plugins/Admin/templates/Products/detect_missing_product_images.php +++ b/plugins/Admin/templates/Products/detect_missing_product_images.php @@ -1,4 +1,6 @@ Html->link($product->name, $this->Slug->getProductDetail($product->id_product, $product->name)); - $outputHtml .= ' / ' . $this->Html->link('Admin', $this->Slug->getProductAdmin($product->id_manufacturer, $product->id_product)); - $outputHtml .= ' / ' . $product->manufacturer->name . '
'; + $outputHtml .= '
'; + $outputHtml .= $this->Html->link($product->name, $this->Slug->getProductDetail($product->id_product, $product->name)); + $outputHtml .= ' / ' . $this->Html->link('Admin', $this->Slug->getProductAdmin($product->id_manufacturer, $product->id_product)); + $outputHtml .= ' / ' . $product->manufacturer->name; + $outputHtml .= ' / ImageId: ' . $product->image->id_image . '
'; + $outputHtml .= '
'; } } } diff --git a/plugins/Admin/templates/Products/index.php b/plugins/Admin/templates/Products/index.php index 263f9413e3..2423921b3c 100644 --- a/plugins/Admin/templates/Products/index.php +++ b/plugins/Admin/templates/Products/index.php @@ -1,4 +1,6 @@ @@ -341,12 +344,14 @@ echo ''; echo '
'; - echo $this->element('productList/button/deleteSelectedProducts'); - echo $this->element('productList/button/calculateSellingPriceWithSurchargeForSelectedProducts'); - echo $this->element('productList/button/generateProductCardsOfSelectedProducts'); - echo $this->element('productList/button/editDeliveryRhythmForSelectedProducts', [ - 'products' => $products - ]); + if (!empty($products)) { + echo '
' . __d('admin', 'Selected_products') . ':
'; + echo $this->element('productList/button/deleteSelectedProducts'); + echo $this->element('productList/button/calculateSellingPriceWithSurchargeForSelectedProducts'); + echo $this->element('productList/button/generateProductCardsOfSelectedProducts'); + echo $this->element('productList/button/editStatusForSelectedProducts'); + echo $this->element('productList/button/editDeliveryRhythmForSelectedProducts'); + } echo '
'; ?> @@ -411,7 +416,7 @@ echo $this->Form->control('Weekdays', [ 'type' => 'select', 'label' => '', - 'options' => $this->Time->getSendOrderListsWeekdayOptions() + 'options' => $this->Html->getSendOrderListsWeekdayOptions() ]); echo '
'; diff --git a/plugins/Admin/templates/Reports/payments.php b/plugins/Admin/templates/Reports/payments.php index b41003909a..970022e0d4 100644 --- a/plugins/Admin/templates/Reports/payments.php +++ b/plugins/Admin/templates/Reports/payments.php @@ -1,4 +1,6 @@ element('reportNavTabs', [ +echo $this->element('navTabs/reportNavTabs', [ 'key' => $this->request->getParam('pass')[0], 'dateFrom' => $dateFrom, 'dateTo' => $dateTo, @@ -108,16 +110,12 @@ } echo ''; echo ''; - switch ($payment->approval) { - case -1: - echo ''; - break; - case 0: - break; - case 1: - echo ''; - break; - } + echo match($payment->approval) { + -1 => '', + 0 => '', + 1 => '', + }; + if ($payment->approval_comment != '') { echo $this->Html->link( '', @@ -168,12 +166,10 @@ if ($showTextColumn) { echo ''; - switch ($paymentType) { - case 'deposit': - echo $this->Html->getManufacturerDepositPaymentText($payment->text); - break; - default: - echo $payment->text; + if ($paymentType == 'deposit') { + echo $this->Html->getManufacturerDepositPaymentText($payment->text); + } else { + echo $payment->text; } echo ''; } diff --git a/plugins/Admin/templates/Sliders/edit.php b/plugins/Admin/templates/Sliders/edit.php index cd45d1a22b..289c1a7dea 100644 --- a/plugins/Admin/templates/Sliders/edit.php +++ b/plugins/Admin/templates/Sliders/edit.php @@ -1,4 +1,6 @@ id_slider; echo ''; - echo ''; + echo ''; echo ''; echo ''; diff --git a/plugins/Admin/templates/Statistics/index.php b/plugins/Admin/templates/Statistics/index.php index 8c698941cb..cd8185907f 100644 --- a/plugins/Admin/templates/Statistics/index.php +++ b/plugins/Admin/templates/Statistics/index.php @@ -1,4 +1,6 @@ Form->control('year', [ + echo $this->Form->control('range', [ 'type' => 'select', 'label' => '', - 'empty' => __d('admin', 'Show_all_years'), - 'options' => $years, - 'default' => $year != '' ? $year : '' + 'options' => $ranges, + 'default' => $range != '' ? $range : '' ]); ?> @@ -72,7 +73,7 @@ "'" . __d('admin', 'Surcharge') . " %'". ");" ]); -if ($year == '' && count($xAxisDataLineChart) > 1) { +if ($range == '' && count($xAxisDataLineChart) > 1) { $this->element('addScript', [ 'script' => Configure::read('app.jsNamespace') . ".AppChart.initLineChart(".json_encode($xAxisDataLineChart).", ".json_encode($yAxisDataLineChart).");" @@ -91,7 +92,7 @@ if (Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED')) { echo ''; - echo ''; + echo ''; echo ''; echo ''; echo ''; @@ -134,7 +135,7 @@ ?>

- 1) { ?> + 1) { ?> diff --git a/plugins/Admin/templates/Taxes/edit.php b/plugins/Admin/templates/Taxes/edit.php index 450f893d61..d39d5d6874 100644 --- a/plugins/Admin/templates/Taxes/edit.php +++ b/plugins/Admin/templates/Taxes/edit.php @@ -1,4 +1,6 @@ element('addScript', [ - 'script' => Configure::read('app.jsNamespace') . ".Admin.init();" . Configure::read('app.jsNamespace') . ".Admin.initForm(); +$this->element('addScript', [ 'script' => + Configure::read('app.jsNamespace') . ".Admin.init();" . + Configure::read('app.jsNamespace') . ".Admin.initForm();". + Configure::read('app.jsNamespace') . ".Admin.selectMainMenuAdmin('".__d('admin', 'Website_administration')."', '".__d('admin', 'Configurations')."'); " ]); ?> @@ -31,6 +35,11 @@ class="btn btn-outline-light cancel"> +element('navTabs/configurationNavTabs', [ + 'key' => 'tax_rates', + ]); +?>
element('addScript', [ - 'script' => Configure::read('app.jsNamespace') . ".Admin.init();" + $this->element('addScript', [ 'script' => + Configure::read('app.jsNamespace') . ".Admin.init();" . + Configure::read('app.jsNamespace') . ".Admin.selectMainMenuAdmin('".__d('admin', 'Website_administration')."', '".__d('admin', 'Configurations')."'); + " ]); $this->element('highlightRowAfterEdit', [ 'rowIdPrefix' => '#tax-' @@ -43,6 +47,9 @@ element('navTabs/configurationNavTabs', [ + 'key' => 'tax_rates', +]); echo '
' . __d('admin', 'Net_turnover') . '' . __d('admin', 'Net_turnover_selling_price') . ''. __d('admin', 'total') . '' . $this->Number->formatAsCurrency($totalNetTurnover) . '
'; echo ''; diff --git a/plugins/Admin/templates/element/addDepositPaymentOverlay.php b/plugins/Admin/templates/element/addDepositPaymentOverlay.php index 8447ce43db..30987869d2 100644 --- a/plugins/Admin/templates/element/addDepositPaymentOverlay.php +++ b/plugins/Admin/templates/element/addDepositPaymentOverlay.php @@ -1,4 +1,6 @@ isSuperadmin()) { - $homepageAdministrationElement['children'][] = [ - 'slug' => $this->Slug->getTaxesList(), - 'name' => __d('admin', 'Tax_rates'), - 'options' => [ - 'fa-icon' => 'fa-fw ok fa-percent' - ] - ]; + $reportSlug = null; if ($this->Html->paymentIsCashless()) { @@ -213,6 +209,7 @@ 'fa-icon' => 'fa-fw ok fa-cogs' ] ]; + } $menu[] = $homepageAdministrationElement; diff --git a/src/Model/Traits/ProductCacheClearAfterSaveTrait.php b/plugins/Admin/templates/element/navTabs/configurationNavTabs.php similarity index 57% rename from src/Model/Traits/ProductCacheClearAfterSaveTrait.php rename to plugins/Admin/templates/element/navTabs/configurationNavTabs.php index 0ac325329d..f6f7562f4f 100644 --- a/src/Model/Traits/ProductCacheClearAfterSaveTrait.php +++ b/plugins/Admin/templates/element/navTabs/configurationNavTabs.php @@ -1,11 +1,5 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ -trait ProductCacheClearAfterSaveTrait -{ +?> + diff --git a/plugins/Admin/templates/element/reportNavTabs.php b/plugins/Admin/templates/element/navTabs/reportNavTabs.php similarity index 97% rename from plugins/Admin/templates/element/reportNavTabs.php rename to plugins/Admin/templates/element/navTabs/reportNavTabs.php index 1bde75f1cc..d1db293491 100644 --- a/plugins/Admin/templates/element/reportNavTabs.php +++ b/plugins/Admin/templates/element/navTabs/reportNavTabs.php @@ -1,4 +1,6 @@ '; -} + } + $customerName = $this->Html->getNameRespectingIsDeleted($orderDetail->customer); + if ($appAuth->isManufacturer() && $appAuth->getManufacturerAnonymizeCustomers()) { + $customerName = $this->Html->anonymizeCustomerName($customerName, $orderDetail->id_customer); + } + echo '' . $customerName . ''; + echo '' . $orderDetail->id_customer . ''; +echo ''; ?> \ No newline at end of file diff --git a/plugins/Admin/templates/element/orderDetailList/data/deposit.php b/plugins/Admin/templates/element/orderDetailList/data/deposit.php index bc58547d0a..67a54cf80b 100644 --- a/plugins/Admin/templates/element/orderDetailList/data/deposit.php +++ b/plugins/Admin/templates/element/orderDetailList/data/deposit.php @@ -1,4 +1,6 @@ AddPage(); $i = 0; -$pdf->table = '
'; + if ($editRecordAllowed && ($appAuth->isAdmin() || $appAuth->isSuperadmin()) - && $this->Html->getNameRespectingIsDeleted($orderDetail->customer) != $this->Html->getDeletedCustomerName()) { + && $this->Html->getNameRespectingIsDeleted($orderDetail->customer) + != $this->Html->getDeletedCustomerName()) { echo $this->Html->link( '', 'javascript:void(0);', @@ -28,10 +33,13 @@ 'escape' => false ] ); - } - echo '' . $this->Html->getNameRespectingIsDeleted($orderDetail->customer) . ''; - echo '' . $orderDetail->id_customer . ''; - echo '
'; +$pdf->table = '
'; foreach($customers as $customer) { $pairRecord = $i % 2 == 0; if ($pairRecord) { $pdf->table .= ''; } - $pdf->table .= '
'; // roughly 85,60mm x 53,98mm + $pdf->table .= ''; // roughly 85,60mm x 53,98mm $pdf->table .= ''; // START ROW logo and name block @@ -55,7 +57,8 @@ // START ROW barcode and customer image $pdf->table .= ''; - $pdf->table .= ''; //spacer left of barcode + $pdf->table .= ''; $pdf->table .= ''; //spacer between barcode and customer image // move user image to bottom - $pdf->table .= ' diff --git a/plugins/Admin/templates/email/html/credit_csv_upload_successful.php b/plugins/Admin/templates/email/html/credit_csv_upload_successful.php index be54cffbf3..b78d1e15ab 100644 --- a/plugins/Admin/templates/email/html/credit_csv_upload_successful.php +++ b/plugins/Admin/templates/email/html/credit_csv_upload_successful.php @@ -1,4 +1,6 @@

- : Slug->getCustomerProfile(); ?> + : Slug->getCustomerProfile(); ?>

diff --git a/plugins/Admin/templates/email/html/email_order_reminder.php b/plugins/Admin/templates/email/html/email_order_reminder.php index 50bac8ebdc..ec2c82dd15 100644 --- a/plugins/Admin/templates/email/html/email_order_reminder.php +++ b/plugins/Admin/templates/email/html/email_order_reminder.php @@ -1,4 +1,6 @@


- //addLastOrderToCart + //addLastOrderToCart

- :
+ :

- : Slug->getCustomerProfile(); ?> + : Slug->getCustomerProfile(); ?>

diff --git a/plugins/Admin/templates/email/html/order_detail_amount_changed.php b/plugins/Admin/templates/email/html/order_detail_amount_changed.php index 18df00bc81..e8608ec600 100644 --- a/plugins/Admin/templates/email/html/order_detail_amount_changed.php +++ b/plugins/Admin/templates/email/html/order_detail_amount_changed.php @@ -1,4 +1,6 @@
- Slug->getMyCreditBalance(); ?> + Slug->getMyCreditBalance(); ?>

diff --git a/plugins/Admin/templates/email/html/pickup_reminder.php b/plugins/Admin/templates/email/html/pickup_reminder.php index a16f304d86..1bb0fffa68 100644 --- a/plugins/Admin/templates/email/html/pickup_reminder.php +++ b/plugins/Admin/templates/email/html/pickup_reminder.php @@ -1,4 +1,6 @@

- : Slug->getCustomerProfile(); ?> + : Slug->getCustomerProfile(); ?>

diff --git a/plugins/Admin/templates/email/html/send_cancellation_invoice_to_customer.php b/plugins/Admin/templates/email/html/send_cancellation_invoice_to_customer.php index e3494271b3..8dfdf64ece 100644 --- a/plugins/Admin/templates/email/html/send_cancellation_invoice_to_customer.php +++ b/plugins/Admin/templates/email/html/send_cancellation_invoice_to_customer.php @@ -1,4 +1,6 @@

- : Slug->getCustomerProfile(); ?> + : Slug->getCustomerProfile(); ?>

diff --git a/plugins/Admin/templates/email/html/send_delivery_note.php b/plugins/Admin/templates/email/html/send_delivery_note.php index 05ee8aad56..c39036eed3 100644 --- a/plugins/Admin/templates/email/html/send_delivery_note.php +++ b/plugins/Admin/templates/email/html/send_delivery_note.php @@ -1,4 +1,6 @@

+ 0) { ?> +

+ MyHtml->getCurrencyIsoCode(Configure::read('appDb.FCS_CURRENCY_SYMBOL')); ?> +

+ +

- : Slug->getCustomerProfile(); ?> + : Slug->getCustomerProfile(); ?>

diff --git a/plugins/Admin/templates/email/html/send_invoice_to_manufacturer.php b/plugins/Admin/templates/email/html/send_invoice_to_manufacturer.php index 40aaab7d9a..c117c8bb3a 100644 --- a/plugins/Admin/templates/email/html/send_invoice_to_manufacturer.php +++ b/plugins/Admin/templates/email/html/send_invoice_to_manufacturer.php @@ -1,4 +1,6 @@

- : /admin + : /admin

diff --git a/plugins/Admin/templates/layout/default.php b/plugins/Admin/templates/layout/default.php index 1b551e7f7b..bc37eb5303 100644 --- a/plugins/Admin/templates/layout/default.php +++ b/plugins/Admin/templates/layout/default.php @@ -1,4 +1,6 @@ + @@ -36,15 +39,16 @@ element('jsNamespace'); ?> plugin != 'Admin') { - $cssConfigs[] = $this->plugin.'.all'; - } - echo $this->element('renderCss', ['configs' => $cssConfigs]); + echo $this->element('customCssVars'); + + $renderConfigs = ['admin']; if ($isMobile) { - echo $this->Html->css(['/node_modules/slidebars/dist/slidebars', 'mobile-global', 'Admin.mobile']); + $renderConfigs = ['admin_mobile']; } - echo $this->element('customThemeStyleSheet'); + if ($this->plugin != 'Admin') { + $renderConfigs[] = $this->plugin.'.all'; + } + echo $this->element('renderCss', ['configs' => $renderConfigs]); echo $this->element('layout/customHeader'); ?> @@ -89,9 +93,8 @@ ); } -echo $this->Html->script('/node_modules/ckeditor4/ckeditor.js?v4.19.1'); -echo $this->Html->script('/node_modules/ckeditor4/adapters/jquery.js?v4.19.1'); - +echo $this->Html->script('/node_modules/ckeditor4/ckeditor.js?v4.21.0'); +echo $this->Html->script('/node_modules/ckeditor4/adapters/jquery.js?v4.21.0'); $scripts = $this->fetch('script'); if ($scripts != '') { echo $this->Html->wrapJavascriptBlock($scripts); diff --git a/plugins/Admin/templates/pdf/invoice_to_customer.php b/plugins/Admin/templates/pdf/invoice_to_customer.php index 473a1dc894..ee155e4f22 100644 --- a/plugins/Admin/templates/pdf/invoice_to_customer.php +++ b/plugins/Admin/templates/pdf/invoice_to_customer.php @@ -1,4 +1,6 @@ Ln(5); - $customerHtml = '

' . $od[0]->customer->name . '

'; + $customerHtml = '

' . $od[0]->customer->name . '

'; $pdf->writeHTML($customerHtml, true, false, true, false, ''); $pdf->writeHTML('

' .__d('admin', 'Pickup_day') . ': ' . $od[0]->pickup_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateLong2')) . ' / ID: ' . $od[0]->customer->id_customer . '

', true, false, true, false, ''); @@ -81,7 +83,7 @@ $oldStorageLocation = $orderDetail->product->id_storage_location; - $pdf->table .= ''; + $pdf->table .= ''; $quantityStyle = ''; if ($orderDetail['product_amount'] > 1) { @@ -138,7 +140,7 @@ if ($i == count($od)) { if (Configure::read('app.isDepositEnabled')) { - $pdf->table .= ''; + $pdf->table .= ''; $pdf->table .= ''; $pdf->table .= ''; $pdf->table .= ''; diff --git a/plugins/Admin/templates/pdf/order_list_by_customer.php b/plugins/Admin/templates/pdf/order_list_by_customer.php index f9da63b28a..42d07ef5ac 100644 --- a/plugins/Admin/templates/pdf/order_list_by_customer.php +++ b/plugins/Admin/templates/pdf/order_list_by_customer.php @@ -1,4 +1,6 @@ AddPage(); $i = 0; -$pdf->table = '
'; + $pdf->table .= ''; $barcodeObject = new TCPDFBarcode($customer->system_bar_code, 'C39'); //https://stackoverflow.com/a/54520065/2100184 $imgBase64Encoded = @base64_encode($barcodeObject->getBarcodePngData(1.5, 102)); @@ -65,7 +68,7 @@ $pdf->table .= ''; + $pdf->table .= ''; $customerImage = Configure::read('app.customerImagesDir') . DS . Configure::read('app.htmlHelper')->getImageFile(Configure::read('app.customerImagesDir'), $customer->id_customer . '-large'); if (file_exists($customerImage)) { $fileinfos = getimagesize($customerImage); diff --git a/plugins/Admin/templates/element/productList/button/calculateSellingPriceWithSurchargeForSelectedProducts.php b/plugins/Admin/templates/element/productList/button/calculateSellingPriceWithSurchargeForSelectedProducts.php index e25ddab4a6..c493a956bd 100644 --- a/plugins/Admin/templates/element/productList/button/calculateSellingPriceWithSurchargeForSelectedProducts.php +++ b/plugins/Admin/templates/element/productList/button/calculateSellingPriceWithSurchargeForSelectedProducts.php @@ -1,4 +1,6 @@ isManufacturer()) { +if (!Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED') || $appAuth->isManufacturer() || empty($products)) { return false; } -if (!empty($products)) { - $this->element('addScript', [ - 'script' => Configure::read('app.jsNamespace').".ModalProductCalculateSellingPriceWithSurcharge.init();" - ]); - echo ' ' . __d('admin', 'Calculate_selling_price_with_surcharge_for_selected_products') . ''; -} - -?> \ No newline at end of file +$this->element('addScript', [ + 'script' => Configure::read('app.jsNamespace') . ".ModalProductCalculateSellingPriceWithSurcharge.init();" +]); +echo ' ' . __d('admin', 'Calculate_selling_price') . ''; diff --git a/plugins/Admin/templates/element/productList/button/deleteSelectedProducts.php b/plugins/Admin/templates/element/productList/button/deleteSelectedProducts.php index a3a58e7607..0f989a46d2 100644 --- a/plugins/Admin/templates/element/productList/button/deleteSelectedProducts.php +++ b/plugins/Admin/templates/element/productList/button/deleteSelectedProducts.php @@ -1,4 +1,6 @@ element('addScript', [ - 'script' => Configure::read('app.jsNamespace').".ModalProductDelete.init();" - ]); - echo ' ' . __d('admin', 'Delete_selected_products') . ''; +if (empty($products)) { + return false; } -?> \ No newline at end of file +$this->element('addScript', [ + 'script' => Configure::read('app.jsNamespace') . ".ModalProductDelete.init();" +]); +echo ' ' . __d('admin', 'Delete') . ''; diff --git a/plugins/Admin/templates/element/productList/button/editDeliveryRhythmForSelectedProducts.php b/plugins/Admin/templates/element/productList/button/editDeliveryRhythmForSelectedProducts.php index aa0503185e..1c4bc83c57 100644 --- a/plugins/Admin/templates/element/productList/button/editDeliveryRhythmForSelectedProducts.php +++ b/plugins/Admin/templates/element/productList/button/editDeliveryRhythmForSelectedProducts.php @@ -1,4 +1,6 @@ element('addScript', [ - 'script' => Configure::read('app.jsNamespace').".ModalProductDeliveryRhythmEdit.initBulk();" - ]); - echo ' ' . __d('admin', 'Edit_delivery_rhythm_for_selected_products') . ''; +if (empty($products)) { + return false; } -?> \ No newline at end of file +$this->element('addScript', [ + 'script' => Configure::read('app.jsNamespace') . ".ModalProductDeliveryRhythmEdit.initBulk();" +]); +echo ' ' . __d('admin', 'Edit_delivery_rhythm') . ''; diff --git a/devtools/db_dump_test.php b/plugins/Admin/templates/element/productList/button/editStatusForSelectedProducts.php old mode 100755 new mode 100644 similarity index 52% rename from devtools/db_dump_test.php rename to plugins/Admin/templates/element/productList/button/editStatusForSelectedProducts.php index cf6d9c06ab..5917a9e131 --- a/devtools/db_dump_test.php +++ b/plugins/Admin/templates/element/productList/button/editStatusForSelectedProducts.php @@ -1,4 +1,7 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ -if (!defined('DATASOURCE')) { - define('DATASOURCE', 'TEST'); -} +use Cake\Core\Configure; -if (!defined('DS')) { - define('DS', DIRECTORY_SEPARATOR); +if (empty($products)) { + return false; } -$locale= $argv[1]; -include realpath(__DIR__) . DS . 'db_dump_common.php'; +$this->element('addScript', [ + 'script' => Configure::read('app.jsNamespace') . ".ModalProductStatusEditBulk.init();" +]); +echo ' ' . __d('admin', 'Edit_status') . ''; diff --git a/plugins/Admin/templates/element/productList/button/generateProductCardsOfSelectedProducts.php b/plugins/Admin/templates/element/productList/button/generateProductCardsOfSelectedProducts.php index c843ed48ed..4d0ed042b0 100644 --- a/plugins/Admin/templates/element/productList/button/generateProductCardsOfSelectedProducts.php +++ b/plugins/Admin/templates/element/productList/button/generateProductCardsOfSelectedProducts.php @@ -1,4 +1,6 @@ isManufacturer()) { +if ($appAuth->isManufacturer() || empty($products) || !Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED')) { return false; } -if (Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED')) { - $this->element('addScript', [ - 'script' => Configure::read('app.jsNamespace').".Admin.initGenerateProductCardsOfSelectedProductsButton();" - ]); - echo ' ' . __d('admin', 'Generate_product_cards') . ''; -} - -?> \ No newline at end of file +$this->element('addScript', [ + 'script' => Configure::read('app.jsNamespace') . ".Admin.initGenerateProductCardsOfSelectedProductsButton();" +]); +echo ' ' . __d('admin', 'Generate_product_cards') . ''; diff --git a/plugins/Admin/templates/element/productList/data/addAttributeButton.php b/plugins/Admin/templates/element/productList/data/addAttributeButton.php index 6e7497a900..f8bb08324c 100644 --- a/plugins/Admin/templates/element/productList/data/addAttributeButton.php +++ b/plugins/Admin/templates/element/productList/data/addAttributeButton.php @@ -1,4 +1,6 @@ '; @@ -67,7 +70,7 @@ echo $sendOrderListWeekdayElement; if ($product->delivery_rhythm_type != 'individual') { - if ($product->delivery_rhythm_send_order_list_weekday != $this->Time->getSendOrderListsWeekday()) { + if ($product->delivery_rhythm_send_order_list_weekday != DeliveryRhythm::getSendOrderListsWeekday()) { $elementsToRender[] = __d('admin', 'Last_order_weekday') . ': ' . $this->Time->getWeekdayName($lastOrderWeekday) . ' ' . __d('admin', 'midnight'); } } diff --git a/plugins/Admin/templates/element/productList/data/deposit.php b/plugins/Admin/templates/element/productList/data/deposit.php index 7d87f2bf07..864a0cc123 100644 --- a/plugins/Admin/templates/element/productList/data/deposit.php +++ b/plugins/Admin/templates/element/productList/data/deposit.php @@ -1,4 +1,6 @@ '; if ($enabled) { - $this->element('addScript', [ - 'script' => Configure::read('app.jsNamespace') . ".Admin.initRowMarkerAll();" - ]); echo ''; } echo ''; diff --git a/plugins/Admin/templates/email/html/accounting_information_invoices_sent.php b/plugins/Admin/templates/email/html/accounting_information_invoices_sent.php index ec5b39a618..8ea27acc05 100644 --- a/plugins/Admin/templates/email/html/accounting_information_invoices_sent.php +++ b/plugins/Admin/templates/email/html/accounting_information_invoices_sent.php @@ -1,4 +1,6 @@
MyTime->formatToDateShort($cronjobRunDay); ?> - Slug->getActionLogsList() . '?types[]=cronjob_send_invoices&dateFrom='.$formattedCurrentDay.'&dateTo='.$formattedCurrentDay; ?> + Slug->getActionLogsList() . '?types[]=cronjob_send_invoices&dateFrom='.$formattedCurrentDay.'&dateTo='.$formattedCurrentDay; ?>

MyHtml->paymentIsCashless()) { ?>


- Slug->getReport('product'); ?> + Slug->getReport('product'); ?>

diff --git a/plugins/Admin/templates/email/html/check_credit_balance.php b/plugins/Admin/templates/email/html/check_credit_balance.php index 8a114eca0c..fcddc0cfab 100644 --- a/plugins/Admin/templates/email/html/check_credit_balance.php +++ b/plugins/Admin/templates/email/html/check_credit_balance.php @@ -1,4 +1,6 @@


- Slug->getMyCreditBalance(); ?> + Slug->getMyCreditBalance(); ?>

- : Slug->getCustomerProfile(); ?> + : Slug->getCustomerProfile(); ?>

'; +$pdf->table = '
'; foreach($products as $product) { $pairRecord = $i % 2 == 0; if ($pairRecord) { $pdf->table .= ''; } - $pdf->table .= ' diff --git a/templates/email/html/customer_registered_inactive.php b/templates/email/html/customer_registered_inactive.php index affa0143f1..025a818916 100644 --- a/templates/email/html/customer_registered_inactive.php +++ b/templates/email/html/customer_registered_inactive.php @@ -1,4 +1,6 @@ '.$data->firstname . ' ' . $data->lastname . '', $data->email, ''.$data->address_customer->city.'']); ?>

- +

: diff --git a/templates/email/html/debug.php b/templates/email/html/debug.php index 3283c15a1c..cbac8284a1 100644 --- a/templates/email/html/debug.php +++ b/templates/email/html/debug.php @@ -1,4 +1,6 @@ :
- Slug->getActivateNewPassword($activateNewPasswordCode); ?> + Slug->getActivateNewPassword($activateNewPasswordCode); ?>

diff --git a/templates/email/html/order_comment_notification.php b/templates/email/html/order_comment_notification.php new file mode 100644 index 0000000000..9fa41948df --- /dev/null +++ b/templates/email/html/order_comment_notification.php @@ -0,0 +1,38 @@ + + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +echo $this->element('email/tableHead'); ?> + + + + + +element('email/tableFoot'); ?> diff --git a/templates/email/html/order_successful.php b/templates/email/html/order_successful.php index de44bd3091..c7d96fd930 100644 --- a/templates/email/html/order_successful.php +++ b/templates/email/html/order_successful.php @@ -1,4 +1,6 @@ user('newsletter_enabled')) { echo ''; } ?> diff --git a/templates/email/html/order_successful_self_service.php b/templates/email/html/order_successful_self_service.php index 5655777271..9c7343d0c4 100644 --- a/templates/email/html/order_successful_self_service.php +++ b/templates/email/html/order_successful_self_service.php @@ -1,4 +1,6 @@ user('newsletter_enabled')) { echo ''; } ?> diff --git a/templates/email/html/send_test_email_template.php b/templates/email/html/send_test_email_template.php index 36994c78ab..fcbc475403 100644 --- a/templates/email/html/send_test_email_template.php +++ b/templates/email/html/send_test_email_template.php @@ -1,4 +1,6 @@ ' . $this->MyNumber->formatAsDecimal($stockAvailable->sold_out_limit, 0) . ''; ?>

- + +

diff --git a/templates/jpg/profile_image.php b/templates/jpg/profile_image.php index 9328a1e70d..d116fb9be4 100644 --- a/templates/jpg/profile_image.php +++ b/templates/jpg/profile_image.php @@ -1,4 +1,6 @@ @@ -47,11 +49,11 @@ '); + + } + public function testContentOfInvoiceForCompany() { @@ -98,6 +119,7 @@ public function testSendInvoicesWithExcludedFutureOrder() $this->changeConfiguration('FCS_SEND_INVOICES_TO_CUSTOMERS', 1); $this->loginAsSuperadmin(); + Configure::write('app.paypalMeUsername', 'username'); $customerId = Configure::read('test.superadminId'); $this->prepareOrdersAndPaymentsForInvoice($customerId); @@ -106,12 +128,9 @@ public function testSendInvoicesWithExcludedFutureOrder() // move one order detail in future - must be excluded from invoice $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); - $query = 'UPDATE ' . $this->OrderDetail->getTable().' SET pickup_day = :pickupDay WHERE id_order_detail IN(1);'; - $params = [ - 'pickupDay' => '2018-02-09', - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $orderDetailEntity = $this->OrderDetail->get(1); + $orderDetailEntity->pickup_day = '2018-02-09'; + $this->OrderDetail->save($orderDetailEntity); $this->Invoice = $this->getTableLocator()->get('Invoices'); @@ -153,6 +172,7 @@ public function testSendInvoicesWithExcludedFutureOrder() $this->assertMailSentToAt(1, Configure::read('test.loginEmailSuperadmin')); $this->assertMailSubjectContainsAt(1, 'Rechnung Nr. 2018-000001, 02.02.2018'); $this->assertMailContainsHtmlAt(1, 'Guthaben beträgt 61,97 €'); + $this->assertMailContainsHtmlAt(1, 'https://paypal.me/username/35.71EUR'); $this->assertMailContainsAttachment($pdfFilenameWithoutPath); $this->getAndAssertOrderDetailsAfterInvoiceGeneration($invoice->id, 4); diff --git a/tests/TestCase/src/Shell/SendInvoicesToManufacturersShellTest.php b/tests/TestCase/src/Command/SendInvoicesToManufacturersCommandTest.php similarity index 97% rename from tests/TestCase/src/Shell/SendInvoicesToManufacturersShellTest.php rename to tests/TestCase/src/Command/SendInvoicesToManufacturersCommandTest.php index 2d9cdccd0b..f8e5b0179a 100644 --- a/tests/TestCase/src/Shell/SendInvoicesToManufacturersShellTest.php +++ b/tests/TestCase/src/Command/SendInvoicesToManufacturersCommandTest.php @@ -1,4 +1,6 @@ changeManufacturer(5, 'anonymize_customers', 1); + $this->loginAsSuperadmin(); $productId = '346'; // artischocke @@ -62,7 +72,7 @@ public function testSendOrderListsIfOneOrderAvailable() $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; $cronjobRunDay = '2019-02-27'; - $pickupDay = Configure::read('app.timeHelper')->getNextDeliveryDay(strtotime($cronjobRunDay)); + $pickupDay = DeliveryRhythm::getNextDeliveryDay(strtotime($cronjobRunDay)); $this->OrderDetail->save( $this->OrderDetail->patchEntity( @@ -80,26 +90,30 @@ public function testSendOrderListsIfOneOrderAvailable() $this->assertMailCount(2); - $pickupDayFormated = new FrozenDate($pickupDay); - $pickupDayFormated = $pickupDayFormated->i18nFormat( + $pickupDayFormatted = new FrozenDate($pickupDay); + $pickupDayFormatted = $pickupDayFormatted->i18nFormat( Configure::read('app.timeHelper')->getI18Format('DateLong2') ); - $this->assertMailSubjectContainsAt(1, 'Bestellungen für den ' . $pickupDayFormated); + $this->assertMailSubjectContainsAt(1, 'Bestellungen für den ' . $pickupDayFormatted); $this->assertMailContainsAt(1, 'im Anhang findest du zwei Bestelllisten'); - $pickupDayFormated = new FrozenDate($pickupDay); - $pickupDayFormated = $pickupDayFormated->i18nFormat( + $pickupDayFormatted = new FrozenDate($pickupDay); + $pickupDayFormatted = $pickupDayFormatted->i18nFormat( Configure::read('app.timeHelper')->getI18Format('DateLong2') ); $this->assertEquals(2, count(TestEmailTransport::getMessages()[1]->getAttachments())); $this->assertMailSentToAt(1, Configure::read('test.loginEmailVegetableManufacturer')); + + $this->assertGenerationOfOrderLists('2019'.DS.'03', [0,1], [2,3]); + } public function testSendOrderListsIfMoreOrdersAvailable() { $cronjobRunDay = '2018-01-31'; - $pickupDay = Configure::read('app.timeHelper')->getNextDeliveryDay(strtotime($cronjobRunDay)); + $pickupDay = DeliveryRhythm::getNextDeliveryDay(strtotime($cronjobRunDay)); + $this->changeManufacturer(5, 'anonymize_customers', 1); $this->exec('send_order_lists ' . $cronjobRunDay); $this->runAndAssertQueue(); @@ -110,23 +124,26 @@ public function testSendOrderListsIfMoreOrdersAvailable() $this->assertMailCount(3); - $pickupDayFormated = new FrozenDate($pickupDay); - $pickupDayFormated = $pickupDayFormated->i18nFormat( + $pickupDayFormatted = new FrozenDate($pickupDay); + $pickupDayFormatted = $pickupDayFormatted->i18nFormat( Configure::read('app.timeHelper')->getI18Format('DateLong2') ); - $this->assertMailSubjectContainsAt(1, 'Bestellungen für den ' . $pickupDayFormated); + $this->assertMailSubjectContainsAt(1, 'Bestellungen für den ' . $pickupDayFormatted); $this->assertMailContainsAt(1, 'im Anhang findest du zwei Bestelllisten'); $this->assertEquals(2, count(TestEmailTransport::getMessages()[1]->getAttachments())); $this->assertMailSentToAt(1, Configure::read('test.loginEmailVegetableManufacturer')); + + $this->assertGenerationOfOrderLists('2018'.DS.'02', [0,1,2,3,4,5], [6,7]); } public function testSendOrderListsWithSendOrderListFalse() { $cronjobRunDay = '2018-01-31'; - $pickupDay = Configure::read('app.timeHelper')->getNextDeliveryDay(strtotime($cronjobRunDay)); + $pickupDay = DeliveryRhythm::getNextDeliveryDay(strtotime($cronjobRunDay)); + $this->changeManufacturer(5, 'anonymize_customers', 1); $this->changeManufacturer(4, 'send_order_list', 0); $this->runAndAssertQueue(); @@ -139,17 +156,19 @@ public function testSendOrderListsWithSendOrderListFalse() $this->assertMailCount(2); - $pickupDayFormated = new FrozenDate($pickupDay); - $pickupDayFormated = $pickupDayFormated->i18nFormat( + $pickupDayFormatted = new FrozenDate($pickupDay); + $pickupDayFormatted = $pickupDayFormatted->i18nFormat( Configure::read('app.timeHelper')->getI18Format('DateLong2') - ); + ); - $this->assertMailSubjectContainsAt(1, 'Bestellungen für den ' . $pickupDayFormated); + $this->assertMailSubjectContainsAt(1, 'Bestellungen für den ' . $pickupDayFormatted); $this->assertMailContainsAt(1, 'im Anhang findest du zwei Bestelllisten'); $this->assertEquals(2, count(TestEmailTransport::getMessages()[1]->getAttachments())); $this->assertMailSentToAt(0, Configure::read('test.loginEmailVegetableManufacturer')); + $this->assertGenerationOfOrderLists('2018'.DS.'02', [0,1,2,3,4,5], [6,7]); + } public function testSendOrderListsWithIndividualSendOrderListWeekday() @@ -157,6 +176,7 @@ public function testSendOrderListsWithIndividualSendOrderListWeekday() $cronjobRunDay = '2018-01-30'; $productId = 346; $orderDetailId = 1; + $this->changeManufacturer(5, 'anonymize_customers', 1); // 1) run cronjob and assert no changings $this->exec('send_order_lists ' . $cronjobRunDay); @@ -193,10 +213,14 @@ public function testSendOrderListsWithIndividualSendOrderListWeekday() ])->toArray(); $this->assertRegExpWithUnquotedString('Demo Gemüse-Hersteller: 1 Produkt / 1,82 €
Verschickte Bestelllisten: 1', $actionLogs[1]->text); + $this->assertGenerationOfOrderLists('2018'.DS.'02', [0,1], [2,3]); + } public function testSendOrderListsWithDifferentIndividualSendOrderListDayAndWeeklySendDay() { + + $this->changeManufacturer(5, 'anonymize_customers', 1); $this->loginAsSuperadmin(); $productId = 346; $orderDetailIdIndividualDate = 1; @@ -275,6 +299,8 @@ public function testSendOrderListsWithDifferentIndividualSendOrderListDayAndWeek $this->assertMailCount(3); + $this->assertGenerationOfOrderLists('2019'.DS.'10', [0,1,2,3], [4,5,6,7]); + } public function testSendOrderListsWithEmptyIndividualSendOrderListDay() @@ -355,6 +381,18 @@ public function testSendOrderListWithoutStockProducts() } + public function testContentOfOrderListWithoutPricePerUnitAnonymized() + { + $this->changeManufacturer(4, 'anonymize_customers', 1); + $this->loginAsSuperadmin(); + $this->get('/admin/manufacturers/getOrderListByProduct.pdf?manufacturerId=4&pickupDay=02.02.2018&isAnonymized=1&outputType=html'); + $this->assertResponseContains('D.S. - ID 92'); + $this->assertResponseNotContains('Demo Superadmin'); + $this->get('/admin/manufacturers/getOrderListByCustomer.pdf?manufacturerId=4&pickupDay=02.02.2018&isAnonymized=1&outputType=html'); + $this->assertResponseContains('D.S. - ID 92'); + $this->assertResponseNotContains('Demo Superadmin'); + } + public function testContentOfOrderListWithoutPricePerUnit() { $this->loginAsSuperadmin(); @@ -368,7 +406,6 @@ public function testContentOfOrderListWithoutPricePerUnit() $expectedResult = file_get_contents(TESTS . 'config' . DS . 'data' . DS . 'orderListByCustomerWithoutPricePerUnit.html'); $expectedResult = $this->getCorrectedLogoPathInHtmlForPdfs($expectedResult); $this->assertResponseContains($expectedResult); - } public function testContentOfOrderListWithoutPricePerUnitAndPurchasePriceEnabled() @@ -482,6 +519,29 @@ private function assertOrderDetailState($orderDetailId, $expectedOrderState) $this->assertEquals($expectedOrderState, $newOrderDetail->order_state); } + private function assertGenerationOfOrderLists(string $datePath, array $clearText, array $anonymous) + { + $path = realpath(Configure::read('app.folder_order_lists') . DS . $datePath); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::SELF_FIRST); + + $files = []; + foreach ($objects as $name => $object) { + if (!preg_match('/\.pdf$/', $name)) { + continue; + } + $files[] = str_replace(Configure::read('app.folder_order_lists'), '', $object->getPathName()); + } + sort($files); + + $this->assertEquals(count($clearText) + count($anonymous), count($files)); + foreach($clearText as $clearTextIndex) { + $this->assertDoesNotMatchRegularExpression('/anonymized/', $files[$clearTextIndex]); + } + foreach($anonymous as $anonymousIndex) { + $this->assertMatchesRegularExpression('/anonymized/', $files[$anonymousIndex]); + } + } + public function tearDown(): void { parent::tearDown(); diff --git a/tests/TestCase/src/Controller/BlogPostsControllerTest.php b/tests/TestCase/src/Controller/BlogPostsControllerTest.php index 17824e8be7..e2a077516c 100644 --- a/tests/TestCase/src/Controller/BlogPostsControllerTest.php +++ b/tests/TestCase/src/Controller/BlogPostsControllerTest.php @@ -1,4 +1,6 @@ loginAsSuperadmin(); - $this->changeProductDeliveryRhythm($this->productId1, '0-individual', '2018-12-14', '2018-07-12'); + $this->changeProductDeliveryRhythm((int) $this->productId1, '0-individual', '2018-12-14', '2018-07-12'); $response = $this->addProductToCart($this->productId1, 1); $this->assertRegExpWithUnquotedString('Das Produkt Artischocke kann nicht mehr bestellt werden.', $response->msg); $this->assertJsonError(); @@ -163,7 +172,7 @@ public function testAddProductDeliveryRhythmIndividualOrderNotPossibleAnyMore() public function testAddProductDeliveryRhythmIndividualOrderPossible() { $this->loginAsSuperadmin(); - $this->changeProductDeliveryRhythm($this->productId1, '0-individual', '2035-12-14', '2035-07-12'); + $this->changeProductDeliveryRhythm((int) $this->productId1, '0-individual', '2035-12-14', '2035-07-12'); $this->addProductToCart($this->productId1, 1); $this->assertJsonOk(); } @@ -302,17 +311,22 @@ public function testAddedProductWithoutAttributesInCartAndOnFinishProductHasAttr $this->ProductAttribute->add($productId, 35); $this->finishCart(); $this->checkValidationError(); - $this->assertRegExpWithUnquotedString('Dem Produkt wurden in der Zwischenzeit Varianten hinzugef', $this->_response); + $this->assertRegExpWithUnquotedString('Dem Produkt wurden in der Zwischenzeit Varianten hinzugef', $this->_response->getBody()->__toString()); } public function testRemoveProductIfProductAttributeWasDeletedAndOtherProductAttributesExistAfterAddingToCart() { $this->loginAsCustomer(); $this->addProductToCart($this->productId2, 1); - $query = 'UPDATE ' . $this->Product->getTable().' SET active = 0 WHERE id_product = 60'; - $this->dbConnection->execute($query); - $query = 'UPDATE ' . $this->Cart->CartProducts->getTable().' SET id_product_attribute = 5000 WHERE id_cart_product = 3'; - $this->dbConnection->execute($query); + + $productEntity = $this->Product->get(60); + $productEntity->active = APP_OFF; + $this->Product->save($productEntity); + + $cpEntity = $this->Cart->CartProducts->get(3); + $cpEntity->id_product_attribute = 5000; + $this->Cart->CartProducts->save($cpEntity); + $this->removeProduct($this->productId2); $cart = $this->Cart->getCart($this, $this->Cart::CART_TYPE_WEEKLY_RHYTHM); $this->assertEquals([], $cart['CartProducts'], 'cart must be empty'); @@ -369,7 +383,7 @@ public function testProductDeactivatedWhileShopping() $this->changeProductStatus($this->productId1, APP_OFF); $this->finishCart(); $this->checkValidationError(); - $this->assertMatchesRegularExpression('/Das Produkt (.*) ist leider nicht mehr aktiviert und somit nicht mehr bestellbar./', $this->_response); + $this->assertMatchesRegularExpression('/Das Produkt (.*) ist leider nicht mehr aktiviert und somit nicht mehr bestellbar./', $this->_response->getBody()->__toString()); $this->changeProductStatus($this->productId1, APP_ON); } @@ -383,7 +397,7 @@ public function testManufacturerDeactivatedWhileShopping() $this->changeManufacturerStatus($manufacturerId, APP_OFF); $this->finishCart(); $this->checkValidationError(); - $this->assertMatchesRegularExpression('/Der Hersteller des Produktes (.*) hat entweder Lieferpause oder er ist nicht mehr aktiviert und das Produkt ist somit nicht mehr bestellbar./', $this->_response); + $this->assertMatchesRegularExpression('/Der Hersteller des Produktes (.*) hat entweder Lieferpause oder er ist nicht mehr aktiviert und das Produkt ist somit nicht mehr bestellbar./', $this->_response->getBody()->__toString()); $this->changeManufacturerStatus($manufacturerId, APP_ON); } @@ -394,11 +408,35 @@ public function testManufacturerDeliveryBreakActivatedWhileShopping() $this->checkCartStatus(); $manufacturerId = 5; - $this->changeManufacturerNoDeliveryDays($manufacturerId, Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb()); + $this->changeManufacturerNoDeliveryDays($manufacturerId, DeliveryRhythm::getDeliveryDateByCurrentDayForDb()); $this->finishCart(); $this->checkValidationError(); - $this->assertMatchesRegularExpression('/Der Hersteller des Produktes (.*) hat entweder Lieferpause oder er ist nicht mehr aktiviert und das Produkt ist somit nicht mehr bestellbar./', $this->_response); - $this->changeManufacturerNoDeliveryDays($manufacturerId); + $this->assertMatchesRegularExpression('/Der Hersteller des Produktes (.*) hat entweder Lieferpause oder er ist nicht mehr aktiviert und das Produkt ist somit nicht mehr bestellbar./', $this->_response->getBody()->__toString()); + } + + public function testManufacturerDeliveryBreakActivatedWhileShoppingWithStockProduct() + { + $this->loginAsSuperadmin(); + $this->addProductToCart($this->productId3, 1); + $this->checkCartStatus(); + + $this->Product->save( + $this->Product->patchEntity( + $this->Product->get($this->productId3), + [ + 'is_stock_product' => '1', + ] + ) + ); + + $manufacturerId = 5; + $this->changeManufacturerNoDeliveryDays($manufacturerId, DeliveryRhythm::getDeliveryDateByCurrentDayForDb()); + $this->finishCart(); + + $cartId = Configure::read('app.htmlHelper')->getCartIdFromCartFinishedUrl($this->_response->getHeaderLine('Location')); + $this->checkCartStatusAfterFinish(); + $cart = $this->getCartById($cartId); + $this->assertEquals($this->productId3, $cart->cart_products[0]->id_product); } public function testGlobalDeliveryBreakActivatedWhileShopping() @@ -406,11 +444,11 @@ public function testGlobalDeliveryBreakActivatedWhileShopping() $this->loginAsSuperadmin(); $this->fillCart(); $this->checkCartStatus(); - $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb()); + $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', DeliveryRhythm::getDeliveryDateByCurrentDayForDb()); $this->loginAsSuperadmin(); $this->finishCart(0, 0); $this->checkValidationError(); - $this->assertMatchesRegularExpression('/(.*) hat die Lieferpause aktiviert und das Produkt (.*) ist nicht mehr bestellbar./', $this->_response); + $this->assertMatchesRegularExpression('/(.*) hat die Lieferpause aktiviert und das Produkt (.*) ist nicht mehr bestellbar./', $this->_response->getBody()->__toString()); } public function testProductStockAvailableDecreasedWhileShopping() @@ -422,7 +460,7 @@ public function testProductStockAvailableDecreasedWhileShopping() $this->changeStockAvailable($this->productId1, 1); $this->finishCart(); $this->checkValidationError(); - $this->assertMatchesRegularExpression('/Anzahl 2/', $this->_response); + $this->assertMatchesRegularExpression('/Anzahl 2/', $this->_response->getBody()->__toString()); $this->assertResponseContains('Menge: 1'); $this->changeStockAvailable($this->productId1, 98); // reset to old stock available } @@ -436,7 +474,7 @@ public function testAttributeStockAvailableDecreasedWhileShopping() $this->changeStockAvailable($this->productId2, 1); $this->finishCart(); $this->checkValidationError(); - $this->assertMatchesRegularExpression('/Anzahl \3/', $this->_response); + $this->assertMatchesRegularExpression('/Anzahl \3/', $this->_response->getBody()->__toString()); $this->assertResponseContains('Menge: 1'); $this->changeStockAvailable($this->productId2, 20); // reset to old stock available } @@ -544,13 +582,30 @@ public function testFinishWithPurchasePriceIncludingProductsWithoutPurchasePrice $this->addAllDifferentProductTypesToCart(); $this->finishCart(1,1); // product and missing pp per piece - $this->assertMatchesRegularExpression('/Das Produkt (.*)Beuschl(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response); + $this->assertMatchesRegularExpression('/Das Produkt (.*)Beuschl(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response->getBody()->__toString()); // product and missing pp per unit - $this->assertMatchesRegularExpression('/Das Produkt (.*)Forelle(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response); + $this->assertMatchesRegularExpression('/Das Produkt (.*)Forelle(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response->getBody()->__toString()); // attribute and missing pp per piece - $this->assertMatchesRegularExpression('/Die Variante (.*)1 kg(.*) des Produkts (.*)Lagerprodukt mit Varianten(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response); + $this->assertMatchesRegularExpression('/Die Variante (.*)1 kg(.*) des Produkts (.*)Lagerprodukt mit Varianten(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response->getBody()->__toString()); // attribute and missing pp per unit - $this->assertMatchesRegularExpression('/Die Variante (.*)1 kg(.*) des Produkts (.*)Rindfleisch(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response); + $this->assertMatchesRegularExpression('/Die Variante (.*)1 kg(.*) des Produkts (.*)Rindfleisch(.*) kann aufgrund von fehlenden Produktdaten zur Zeit leider nicht bestellt werden./', $this->_response->getBody()->__toString()); + } + + public function testFinishWithPickupDayCommentNotification() + { + $this->changeConfiguration('FCS_SEND_INVOICES_TO_CUSTOMERS', 1); + + $this->loginAsSuperadmin(); + $this->fillCart(); + $this->checkCartStatus(); + + $pickupDayComment = 'this is a valid pickup day comment'; + $this->finishCart(1, 1, $pickupDayComment); + + $this->assertMailCount(2); + $this->assertMailSubjectContainsAt(0, 'Neuer Bestell-Kommentar von Demo Superadmin'); + $this->assertMailContainsAt(0, $pickupDayComment); + } public function testFinishWithPurchasePriceOk() @@ -646,7 +701,7 @@ public function testIsSubscribeNewsletterLinkAddedToMail() $this->loginAsSuperadmin(); $this->fillCart(); $this->finishCart(1, 1); - $this->assertMailContainsAt(0, 'Du kannst unseren Newsletter im Admin-Bereich unter "Meine Daten" abonnieren.'); + $this->assertMailContainsAt(0, 'Du kannst unseren Newsletter im Admin-Bereich unter "Meine Daten" abonnieren.'); } public function testIsSubscribeNewsletterLinkNotAddedToMail() @@ -674,7 +729,7 @@ public function testFinishOrderWithComment() $this->checkCartStatusAfterFinish(); $cart = $this->getCartById($cartId); - $pickupDay = Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb(); + $pickupDay = DeliveryRhythm::getDeliveryDateByCurrentDayForDb(); // check order_details for product1 (index 2!) $this->checkOrderDetails($cart->cart_products[0]->order_detail, 'Artischocke : Stück', 2, 0, 1, 3.3, 3.64, 0.17, 0.34, 10, $pickupDay); @@ -711,7 +766,7 @@ public function testFinishOrderWithComment() public function testProductsWithAllowedNegativeStock() { - $this->changeManufacturer(5, 'stock_management_enabled', true); + $this->changeManufacturer(5, 'stock_management_enabled', 1); $this->loginAsCustomer(); $this->addProductToCart(349, 8); $this->assertJsonOk(); @@ -719,7 +774,7 @@ public function testProductsWithAllowedNegativeStock() public function testProductsWithAllowedNegativeStockButTooHighAmount() { - $this->changeManufacturer(5, 'stock_management_enabled', true); + $this->changeManufacturer(5, 'stock_management_enabled', 1); $this->loginAsCustomer(); $response = $this->addProductToCart(349, 11); $this->assertRegExpWithUnquotedString('Die gewünschte Anzahl 11 des Produktes Lagerprodukt ist leider nicht mehr verfügbar. Verfügbare Menge: 10', $response->msg); @@ -791,7 +846,7 @@ public function testFinishOrderStockNotificationsEnabled() $this->loginAsCustomer(); $manufacturerId = $this->Customer->getManufacturerIdByCustomerId(Configure::read('test.vegetableManufacturerId')); - $this->changeManufacturer($manufacturerId, 'stock_management_enabled', true); + $this->changeManufacturer($manufacturerId, 'stock_management_enabled', 1); $this->placeOrderWithStockProducts(); @@ -831,7 +886,7 @@ public function testFinishCartWithPricePerUnit() $this->checkCartStatusAfterFinish(); $cart = $this->getCartById($cartId); - $pickupDay = Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb(); + $pickupDay = DeliveryRhythm::getDeliveryDateByCurrentDayForDb(); // check order_details $this->checkOrderDetails($cart->cart_products[0]->order_detail, 'Forelle : Stück', 2, 0, 0, 9.54, 10.5, 0.48, 0.96, 10, $pickupDay); @@ -1020,7 +1075,7 @@ public function testInstantOrderOk() public function testInstantOrderWithDeliveryBreak() { - $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb()); + $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', DeliveryRhythm::getDeliveryDateByCurrentDayForDb()); $this->loginAsSuperadmin(); $this->get($this->Slug->getOrderDetailsList().'/initInstantOrder/' . Configure::read('test.customerId')); $this->loginAsSuperadminAddOrderCustomerToSession($_SESSION); @@ -1140,7 +1195,7 @@ private function addTooManyProducts($productId, $amount, $expectedAmount, $expec private function checkValidationError() { - $this->assertMatchesRegularExpression('/initCartErrors()/', $this->_response); + $this->assertMatchesRegularExpression('/initCartErrors()/', $this->_response->getBody()->__toString()); } private function changeStockAvailable($productId, $amount) @@ -1180,25 +1235,16 @@ private function checkOrderDetails($orderDetail, $name, $amount, $productAttribu $this->assertEquals($orderDetail->tax_rate, $taxRate); } - /** - * @param int $productId - * @param int $amount - * @return string - */ - private function changeProductStatus($productId, $status) + private function changeProductStatus($productId, $status): void { $this->Product->changeStatus([[$productId => $status]]); } - private function changeManufacturerStatus($manufacturerId, $status) + private function changeManufacturerStatus($manufacturerId, $status): void { $this->changeManufacturer($manufacturerId, 'active', $status); } - /** - * @param int $productId - * @return string - */ private function removeProduct($productId) { $this->ajaxPost('/warenkorb/ajaxRemove', [ diff --git a/tests/TestCase/src/Controller/Component/StringComponentTest.php b/tests/TestCase/src/Controller/Component/StringComponentTest.php index 763e284565..49728527f5 100644 --- a/tests/TestCase/src/Controller/Component/StringComponentTest.php +++ b/tests/TestCase/src/Controller/Component/StringComponentTest.php @@ -1,4 +1,5 @@ [ 'firstname' => '', 'lastname' => '', @@ -228,7 +229,7 @@ public function testRegistrationWithCompanyUserActive() 'Customers.email' => $email, ], ])->first(); - $this->assertTrue((boolean) $customer->is_company); + $this->assertTrue((bool) $customer->is_company); } public function testRegistrationUserNotActive() diff --git a/tests/TestCase/src/Controller/ManufacturersFrontendControllerTest.php b/tests/TestCase/src/Controller/ManufacturersFrontendControllerTest.php index 5a6aef5d7a..244438104e 100644 --- a/tests/TestCase/src/Controller/ManufacturersFrontendControllerTest.php +++ b/tests/TestCase/src/Controller/ManufacturersFrontendControllerTest.php @@ -1,4 +1,5 @@ '; + protected $manufacturerId = 5; + protected $today; + protected $mustNotBeShownString = 'Lieferpause.'; public function setUp(): void { diff --git a/tests/TestCase/src/Controller/PagesControllerTest.php b/tests/TestCase/src/Controller/PagesControllerTest.php index 470aebea8d..8ba3b79b89 100644 --- a/tests/TestCase/src/Controller/PagesControllerTest.php +++ b/tests/TestCase/src/Controller/PagesControllerTest.php @@ -1,4 +1,5 @@ Slug->getReport('product'), $this->Slug->getReport('payback'), $this->Slug->getReport('deposit'), + $this->Slug->getMyCreditBalance(), $this->Slug->getPaymentEdit(1), $this->Slug->getBlogPostListAdmin(), $this->Slug->getBlogPostAdd(), @@ -111,6 +114,8 @@ public function testAllSuperadminUrls() $this->Network->getSyncDomainEdit(1), $this->Slug->getConfigurationsList(), $this->Slug->getConfigurationEdit(544), + $this->Slug->getCronjobsList(), + $this->Slug->getCronjobEdit(1), ]; $this->assertPagesForErrors($testUrls); @@ -140,6 +145,24 @@ public function testAllManufacturerUrls() $this->logout(); } + public function testAllManufacturerUrlsAnonymized() + { + $this->changeManufacturer(4, 'anonymize_customers', 1); + $this->loginAsMeatManufacturer(); + + $testUrls = [ + $this->Slug->getOrderDetailsList() . '?pickupDay[]=02.02.2018', + ]; + + foreach ($testUrls as $url) { + $this->get($url); + $this->assertResponseNotContains('Demo Superadmin'); + $this->assertResponseContains('D.S. - ID 92'); + } + + $this->assertPagesForErrors($testUrls); + } + public function test404PagesLoggedOut() { $testUrls = [ @@ -213,13 +236,9 @@ protected function assertPagesFor404($testPages) protected function changePage($pageId, $isPrivate = 0, $active = 1) { - $query = 'UPDATE ' . $this->Page->getTable().' SET is_private = :isPrivate, active = :active WHERE id_page = :pageId;'; - $params = [ - 'pageId' => $pageId, - 'isPrivate' => $isPrivate, - 'active' => $active - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $pageEntity = $this->Page->get($pageId); + $pageEntity->active = $active; + $pageEntity->is_private = $isPrivate; + $this->Page->save($pageEntity); } } diff --git a/tests/TestCase/src/Controller/ProductsFrontendControllerTest.php b/tests/TestCase/src/Controller/ProductsFrontendControllerTest.php index 0609d43d6a..ea791a4a09 100644 --- a/tests/TestCase/src/Controller/ProductsFrontendControllerTest.php +++ b/tests/TestCase/src/Controller/ProductsFrontendControllerTest.php @@ -1,4 +1,6 @@ loginAsSuperadmin(); $productId = 346; $manufacturerId = 5; - $this->changeManufacturerNoDeliveryDays($manufacturerId, Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb()); + $this->changeManufacturerNoDeliveryDays($manufacturerId, DeliveryRhythm::getDeliveryDateByCurrentDayForDb()); $this->get($this->Slug->getProductDetail($productId, 'Artischocke')); $this->assertResponseContains(' Lieferpause!'); } @@ -176,11 +182,38 @@ public function testProductDetailHtmlProductCatalogWeekly() 'id_product' => $productId, ], ])->first(); - $nextDeliveryDay = $this->Product->getNextDeliveryDay($product, $this); + $nextDeliveryDay = DeliveryRhythm::getNextDeliveryDayForProduct($product, $this); $pickupDay = Configure::read('app.timeHelper')->getDateFormattedWithWeekday(strtotime($nextDeliveryDay)); $this->assertResponseContains(''.$pickupDay.''); } + public function testProductDetailHtmlProductCatalogShowOrderedProductsTotalAmountInCatalog() + { + Configure::write('app.showOrderedProductsTotalAmountInCatalog', true); + $this->Product = $this->getTableLocator()->get('Products'); + $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); + + $productId = 60; + $product = $this->Product->find('all', [ + 'conditions' => [ + 'id_product' => $productId, + ], + ])->first(); + $nextDeliveryDay = DeliveryRhythm::getNextDeliveryDayForProduct($product, $this); + + $query = 'UPDATE ' . $this->OrderDetail->getTable().' SET pickup_day = :pickupDay WHERE id_order_detail IN (3);'; + $params = [ + 'pickupDay' => $nextDeliveryDay, + ]; + $statement = $this->dbConnection->prepare($query); + $statement->execute($params); + + $this->loginAsCustomer(); + $this->get($this->Slug->getProductDetail($productId, 'Milch')); + $formattedPickupDay = Configure::read('app.timeHelper')->getDateFormattedWithWeekday(strtotime($nextDeliveryDay)); + $this->assertResponseContains('
1
'); + } + public function testProductDetailHtmlProductCatalogInstantOrder() { $this->loginAsSuperadmin(); @@ -195,13 +228,9 @@ public function testProductDetailHtmlProductCatalogInstantOrder() protected function changeProductStatus($productId, $active) { - $query = 'UPDATE ' . $this->Product->getTable().' SET active = :active WHERE id_product = :productId;'; - $params = [ - 'productId' => $productId, - 'active' => $active - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $productEntity = $this->Product->get($productId); + $productEntity->active = $active; + $this->Product->save($productEntity); } } diff --git a/tests/TestCase/src/Controller/SelfServiceControllerTest.php b/tests/TestCase/src/Controller/SelfServiceControllerTest.php index e9fac33784..81b9b9174a 100644 --- a/tests/TestCase/src/Controller/SelfServiceControllerTest.php +++ b/tests/TestCase/src/Controller/SelfServiceControllerTest.php @@ -1,4 +1,6 @@ changeConfiguration('FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED', 1); - $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb()); + $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', DeliveryRhythm::getDeliveryDateByCurrentDayForDb()); $this->loginAsSuperadmin(); $this->addProductToSelfServiceCart('350-15', 1, '1,5'); $this->finishSelfServiceCart(1, 1); @@ -413,7 +421,7 @@ private function getSelfServicePostOptions() 'headers' => [ 'X_REQUESTED_WITH' => 'XMLHttpRequest', 'ACCEPT' => 'application/json', - 'REFERER' => Configure::read('app.cakeServerName') . '/' . __('route_self_service'), + 'REFERER' => Configure::read('App.fullBaseUrl') . '/' . __('route_self_service'), ], ]); } @@ -428,7 +436,7 @@ private function finishSelfServiceCart($generalTermsAndConditionsAccepted, $canc ]; $this->configRequest([ 'headers' => [ - 'REFERER' => Configure::read('app.cakeServerName') . '/' . __('route_self_service'), + 'REFERER' => Configure::read('App.fullBaseUrl') . '/' . __('route_self_service'), ], ]); $this->post( diff --git a/tests/TestCase/src/Lib/Csv/GlsBankBankingReaderTest.php b/tests/TestCase/src/Lib/Csv/GlsBankBankingReaderTest.php index ed171e70c6..1ff3ea33c0 100644 --- a/tests/TestCase/src/Lib/Csv/GlsBankBankingReaderTest.php +++ b/tests/TestCase/src/Lib/Csv/GlsBankBankingReaderTest.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +use App\Lib\DeliveryRhythm\DeliveryRhythm; +use App\Test\TestCase\AppCakeTestCase; +use App\Test\TestCase\Traits\DeliveryRhythmConfigsTrait; +use App\Test\TestCase\Traits\LoginTrait; +use App\View\Helper\MyTimeHelper; +use Cake\I18n\FrozenDate; +use Cake\View\View; +use Cake\TestSuite\IntegrationTestTrait; + +class DeliveryRhythmTest extends AppCakeTestCase +{ + + protected $Product; + protected $MyTimeHelper; + + use DeliveryRhythmConfigsTrait; + use IntegrationTestTrait; + use LoginTrait; + + public function setUp(): void + { + parent::setUp(); + $this->Product = $this->getTableLocator()->get('Products'); + $this->MyTimeHelper = new MyTimeHelper(new View()); + } + + public function testGetOrderPeriodFirstDayByDeliveryDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $this->assertGetOrderPeriodFirstDayByDeliveryDay(strtotime('12.01.2023'), '31.12.2022'); + } + + public function testGetOrderPeriodLastDayByDeliveryDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $this->assertGetOrderPeriodLastDayByDeliveryDay(strtotime('12.01.2023'), '06.01.2023'); + } + + public function testGetOrderPeriodFirstDayByDeliveryDayWednesdayFriday() + { + $this->prepareWednesdayFridayConfig(); + $this->assertGetOrderPeriodFirstDayByDeliveryDay(strtotime('12.01.2023'), '04.01.2023'); + } + + public function testGetOrderPeriodLastDayByDeliveryDayWednesdayFriday() + { + $this->prepareWednesdayFridayConfig(); + $this->assertGetOrderPeriodLastDayByDeliveryDay(strtotime('12.01.2023'), '10.01.2023'); + } + + public function testGetDeliveryDayTuesdayFriday() + { + $this->prepareTuesdayFridayConfig(); + $this->assertGetDeliveryDay('25.07.2018', '03.08.2018'); + } + + public function testGetDeliveryDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $this->assertGetDeliveryDay('24.08.2022', '01.09.2022'); // wednesday + $this->assertGetDeliveryDay('25.08.2022', '01.09.2022'); // thursday + $this->assertGetDeliveryDay('26.08.2022', '01.09.2022'); // friday + $this->assertGetDeliveryDay('27.08.2022', '08.09.2022'); // saturday + $this->assertGetDeliveryDay('28.08.2022', '08.09.2022'); // sunday + $this->assertGetDeliveryDay('29.08.2022', '08.09.2022'); // monday + $this->assertGetDeliveryDay('30.08.2022', '08.09.2022'); // tuesday + $this->assertGetDeliveryDay('31.08.2022', '08.09.2022'); // wednesday + } + + public function testGetOrderPeriodFirstDayThursdayFriday() + { + $this->prepareThursdayFridayConfig(); + $this->assertGetOrderPeriodFirstDay('27.11.2017', '23.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('28.11.2017', '23.11.2017'); // tuesday + $this->assertGetOrderPeriodFirstDay('29.11.2017', '23.11.2017'); // wednesday + $this->assertGetOrderPeriodFirstDay('30.11.2017', '23.11.2017'); // thursday + $this->assertGetOrderPeriodFirstDay('01.12.2017', '23.11.2017'); // friday + $this->assertGetOrderPeriodFirstDay('02.12.2017', '30.11.2017'); // saturday + $this->assertGetOrderPeriodFirstDay('03.12.2017', '30.11.2017'); // sunday + $this->assertGetOrderPeriodFirstDay('04.12.2017', '30.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('05.12.2017', '30.11.2017'); // tuesday + } + + public function testGetOrderPeriodFirstDayWednesdayFriday() + { + $this->prepareWednesdayFridayConfig(); + $this->assertGetOrderPeriodFirstDay('27.11.2017', '22.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('28.11.2017', '22.11.2017'); // tuesday + $this->assertGetOrderPeriodFirstDay('29.11.2017', '22.11.2017'); // wednesday + $this->assertGetOrderPeriodFirstDay('30.11.2017', '22.11.2017'); // thursday + $this->assertGetOrderPeriodFirstDay('01.12.2017', '22.11.2017'); // friday + $this->assertGetOrderPeriodFirstDay('02.12.2017', '29.11.2017'); // saturday + $this->assertGetOrderPeriodFirstDay('03.12.2017', '29.11.2017'); // sunday + $this->assertGetOrderPeriodFirstDay('04.12.2017', '29.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('05.12.2017', '29.11.2017'); // tuesday + } + + public function testGetOrderPeriodFirstDayTuesdayFriday() + { + $this->prepareTuesdayFridayConfig(); + $this->assertGetOrderPeriodFirstDay('27.11.2017', '21.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('28.11.2017', '21.11.2017'); // tuesday + $this->assertGetOrderPeriodFirstDay('29.11.2017', '21.11.2017'); // wednesday + $this->assertGetOrderPeriodFirstDay('30.11.2017', '21.11.2017'); // thursday + $this->assertGetOrderPeriodFirstDay('01.12.2017', '21.11.2017'); // friday + $this->assertGetOrderPeriodFirstDay('02.12.2017', '28.11.2017'); // saturday + $this->assertGetOrderPeriodFirstDay('03.12.2017', '28.11.2017'); // sunday + $this->assertGetOrderPeriodFirstDay('04.12.2017', '28.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('05.12.2017', '28.11.2017'); // tuesday + } + + public function testGetOrderPeriodFirstDayMondayTuesday() + { + $this->prepareMondayTuesdayConfig(); + $this->assertGetOrderPeriodFirstDay('27.11.2017', '20.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('28.11.2017', '20.11.2017'); // tuesday + $this->assertGetOrderPeriodFirstDay('29.11.2017', '27.11.2017'); // wednesday + $this->assertGetOrderPeriodFirstDay('30.11.2017', '27.11.2017'); // thursday + $this->assertGetOrderPeriodFirstDay('01.12.2017', '27.11.2017'); // friday + $this->assertGetOrderPeriodFirstDay('02.12.2017', '27.11.2017'); // saturday + $this->assertGetOrderPeriodFirstDay('03.12.2017', '27.11.2017'); // sunday + $this->assertGetOrderPeriodFirstDay('04.12.2017', '27.11.2017'); // monday + $this->assertGetOrderPeriodFirstDay('05.12.2017', '27.11.2017'); // tuesday + } + + public function testGetOrderPeriodFirstDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $this->assertGetOrderPeriodFirstDay('22.08.2022', '20.08.2022'); // monday + $this->assertGetOrderPeriodFirstDay('23.08.2022', '20.08.2022'); // tuesday + $this->assertGetOrderPeriodFirstDay('24.08.2022', '20.08.2022'); // wednesday + $this->assertGetOrderPeriodFirstDay('25.08.2022', '20.08.2022'); // thursday + $this->assertGetOrderPeriodFirstDay('26.08.2022', '20.08.2022'); // friday + $this->assertGetOrderPeriodFirstDay('27.08.2022', '27.08.2022'); // saturday + $this->assertGetOrderPeriodFirstDay('28.08.2022', '27.08.2022'); // sunday + $this->assertGetOrderPeriodFirstDay('29.08.2022', '27.08.2022'); // monday + $this->assertGetOrderPeriodFirstDay('30.08.2022', '27.08.2022'); // tuesday + } + + public function testGetOrderPeriodLastDayThursdayFriday() + { + $this->prepareThursdayFridayConfig(); + $this->assertGetOrderPeriodLastDay('27.11.2017', '29.11.2017'); // monday + $this->assertGetOrderPeriodLastDay('28.11.2017', '29.11.2017'); // tuesday + $this->assertGetOrderPeriodLastDay('29.11.2017', '29.11.2017'); // wednesday + $this->assertGetOrderPeriodLastDay('30.11.2017', '29.11.2017'); // thursday + $this->assertGetOrderPeriodLastDay('01.12.2017', '29.11.2017'); // friday + $this->assertGetOrderPeriodLastDay('02.12.2017', '06.12.2017'); // saturday + $this->assertGetOrderPeriodLastDay('03.12.2017', '06.12.2017'); // sunday + $this->assertGetOrderPeriodLastDay('04.12.2017', '06.12.2017'); // monday + $this->assertGetOrderPeriodLastDay('05.12.2017', '06.12.2017'); // tuesday + } + + public function testGetOrderPeriodLastDayWednesdayFriday() + { + $this->prepareWednesdayFridayConfig(); + $this->assertGetOrderPeriodLastDay('27.11.2017', '28.11.2017'); // monday + $this->assertGetOrderPeriodLastDay('28.11.2017', '28.11.2017'); // tuesday + $this->assertGetOrderPeriodLastDay('29.11.2017', '28.11.2017'); // wednesday + $this->assertGetOrderPeriodLastDay('30.11.2017', '28.11.2017'); // thursday + $this->assertGetOrderPeriodLastDay('01.12.2017', '28.11.2017'); // friday + $this->assertGetOrderPeriodLastDay('02.12.2017', '05.12.2017'); // saturday + $this->assertGetOrderPeriodLastDay('03.12.2017', '05.12.2017'); // sunday + $this->assertGetOrderPeriodLastDay('04.12.2017', '05.12.2017'); // monday + $this->assertGetOrderPeriodLastDay('05.12.2017', '05.12.2017'); // tuesday + } + + public function testGetOrderPeriodLastDayTuesdayFriday() + { + $this->prepareTuesdayFridayConfig(); + $this->assertGetOrderPeriodLastDay('27.11.2017', '27.11.2017'); // monday + $this->assertGetOrderPeriodLastDay('28.11.2017', '27.11.2017'); // tuesday + $this->assertGetOrderPeriodLastDay('29.11.2017', '27.11.2017'); // wednesday + $this->assertGetOrderPeriodLastDay('30.11.2017', '27.11.2017'); // thursday + $this->assertGetOrderPeriodLastDay('01.12.2017', '27.11.2017'); // friday + $this->assertGetOrderPeriodLastDay('02.12.2017', '04.12.2017'); // saturday + $this->assertGetOrderPeriodLastDay('03.12.2017', '04.12.2017'); // sunday + $this->assertGetOrderPeriodLastDay('04.12.2017', '04.12.2017'); // monday + $this->assertGetOrderPeriodLastDay('05.12.2017', '04.12.2017'); // tuesday + } + + public function testGetOrderPeriodLastDayMondayTuesday() + { + $this->prepareMondayTuesdayConfig(); + $this->assertGetOrderPeriodLastDay('27.11.2017', '26.11.2017'); // monday + $this->assertGetOrderPeriodLastDay('28.11.2017', '26.11.2017'); // tuesday + $this->assertGetOrderPeriodLastDay('29.11.2017', '03.12.2017'); // wednesday + $this->assertGetOrderPeriodLastDay('30.11.2017', '03.12.2017'); // thursday + $this->assertGetOrderPeriodLastDay('01.12.2017', '03.12.2017'); // friday + $this->assertGetOrderPeriodLastDay('02.12.2017', '03.12.2017'); // saturday + $this->assertGetOrderPeriodLastDay('03.12.2017', '03.12.2017'); // sunday + $this->assertGetOrderPeriodLastDay('04.12.2017', '03.12.2017'); // monday + $this->assertGetOrderPeriodLastDay('05.12.2017', '03.12.2017'); // tuesday + $this->assertGetOrderPeriodLastDay('06.12.2017', '10.12.2017'); // wednesday + } + + public function testGetOrderPeriodLastDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $this->assertGetOrderPeriodLastDay('29.08.2022', '02.09.2022'); // monday + $this->assertGetOrderPeriodLastDay('30.08.2022', '02.09.2022'); // tuesday + $this->assertGetOrderPeriodLastDay('31.08.2022', '02.09.2022'); // wednesday + $this->assertGetOrderPeriodLastDay('01.09.2022', '02.09.2022'); // thursday + $this->assertGetOrderPeriodLastDay('02.09.2022', '02.09.2022'); // friday + $this->assertGetOrderPeriodLastDay('03.09.2022', '09.09.2022'); // saturday + $this->assertGetOrderPeriodLastDay('04.09.2022', '09.09.2022'); // sunday + $this->assertGetOrderPeriodLastDay('05.09.2022', '09.09.2022'); // monday + $this->assertGetOrderPeriodLastDay('06.09.2022', '09.09.2022'); // tuesday + } + + public function testGetFormattedNextDeliveryDayThursdayFriday() + { + $this->prepareThursdayFridayConfig(); + $this->assertGetFormattedNextDeliveryDay('08.10.2018', '12.10.2018'); // monday + $this->assertGetFormattedNextDeliveryDay('09.10.2018', '12.10.2018'); // tuesday + $this->assertGetFormattedNextDeliveryDay('10.10.2018', '12.10.2018'); // wednesday + $this->assertGetFormattedNextDeliveryDay('11.10.2018', '12.10.2018'); // thursday + $this->assertGetFormattedNextDeliveryDay('12.10.2018', '12.10.2018'); // friday + $this->assertGetFormattedNextDeliveryDay('13.10.2018', '19.10.2018'); // saturday + $this->assertGetFormattedNextDeliveryDay('14.10.2018', '19.10.2018'); // sunday + $this->assertGetFormattedNextDeliveryDay('15.10.2018', '19.10.2018'); // monday + $this->assertGetFormattedNextDeliveryDay('16.10.2018', '19.10.2018'); // tuesday + } + + public function testGetFormattedNextDeliveryDayWednesdayFriday() + { + $this->prepareWednesdayFridayConfig(); + $this->assertGetFormattedNextDeliveryDay('08.10.2018', '12.10.2018'); // monday + $this->assertGetFormattedNextDeliveryDay('09.10.2018', '12.10.2018'); // tuesday + $this->assertGetFormattedNextDeliveryDay('10.10.2018', '12.10.2018'); // wednesday + $this->assertGetFormattedNextDeliveryDay('11.10.2018', '12.10.2018'); // thursday + $this->assertGetFormattedNextDeliveryDay('12.10.2018', '12.10.2018'); // friday + $this->assertGetFormattedNextDeliveryDay('13.10.2018', '19.10.2018'); // saturday + $this->assertGetFormattedNextDeliveryDay('14.10.2018', '19.10.2018'); // sunday + $this->assertGetFormattedNextDeliveryDay('15.10.2018', '19.10.2018'); // monday + $this->assertGetFormattedNextDeliveryDay('16.10.2018', '19.10.2018'); // tuesday + } + + public function testGetFormattedNextDeliveryDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $this->assertGetFormattedNextDeliveryDay('22.08.2022', '25.08.2022'); // monday + $this->assertGetFormattedNextDeliveryDay('23.08.2022', '25.08.2022'); // tuesday + $this->assertGetFormattedNextDeliveryDay('24.08.2022', '25.08.2022'); // wednesday + $this->assertGetFormattedNextDeliveryDay('25.08.2022', '25.08.2022'); // thursday + $this->assertGetFormattedNextDeliveryDay('26.08.2022', '25.08.2022'); // friday + $this->assertGetFormattedNextDeliveryDay('27.08.2022', '01.09.2022'); // saturday + $this->assertGetFormattedNextDeliveryDay('28.08.2022', '01.09.2022'); // sunday + $this->assertGetFormattedNextDeliveryDay('29.08.2022', '01.09.2022'); // monday + $this->assertGetFormattedNextDeliveryDay('30.08.2022', '01.09.2022'); // tuesday + } + + public function testGetLastOrderDayWeeklySendOrderListsDayNormal() + { + $product = [ + 'next_delivery_day' => '2020-12-04', + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => 1, + 'delivery_rhythm_send_order_list_weekday' => 3, + 'delivery_rhythm_order_possible_until' => null, + ]; + $this->assertGetLastOrderDay($product, '2020-12-01'); + } + + public function testGetLastOrderDayWeeklySendOrderListsDayMonday() + { + $product = [ + 'next_delivery_day' => '2020-12-04', + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => 1, + 'delivery_rhythm_send_order_list_weekday' => 2, + 'delivery_rhythm_order_possible_until' => null, + ]; + $this->assertGetLastOrderDay($product, '2020-11-30'); + } + + public function testGetLastOrderDayMonthlySendOrderListsDayNormal() + { + $product = [ + 'next_delivery_day' => '2020-12-25', + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => 0, + 'delivery_rhythm_send_order_list_weekday' => 3, + 'delivery_rhythm_order_possible_until' => null, + ]; + $this->assertGetLastOrderDay($product, '2020-12-22'); + } + + public function testGetLastOrderDayMonthlySendOrderListsDaySunday() + { + $product = [ + 'next_delivery_day' => '2020-12-25', + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => 0, + 'delivery_rhythm_send_order_list_weekday' => 1, + 'delivery_rhythm_order_possible_until' => null, + ]; + $this->assertGetLastOrderDay($product, '2020-12-20'); + } + + public function testGetLastOrderDayIndividual() + { + $product = [ + 'next_delivery_day' => '2020-12-25', + 'delivery_rhythm_type' => 'individual', + 'delivery_rhythm_count' => 0, + 'delivery_rhythm_send_order_list_weekday' => 3, + 'delivery_rhythm_order_possible_until' => new FrozenDate('2020-12-12'), + ]; + $this->assertGetLastOrderDay($product, '2020-12-12'); + } + + public function test1WeekWithFirstDeliveryDayAllowOrdersConfigOff() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-11-02'), + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2018-10-07', + 'result' => '2018-11-02', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekWithFirstDeliveryDayAllowOrdersConfigOn() + { + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-11-02'), + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2018-10-07', + 'result' => 'delivery-rhythm-triggered-delivery-break', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekNormalNoFirstDeliveryDayWednesdayFriday() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2018-10-07', + 'result' => '2018-10-12', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekNormalNoFirstDeliveryDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2022-08-26', // friday + 'result' => '2022-09-01', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekDeliveryDaySaturdayThursday() + { + $this->prepareSaturdayThursdayConfig(); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2022-09-08'), + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2022-08-25', // thursday + 'result' => '2022-09-08', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekNormalNoFirstDeliveryDaySaturdayThursdayWithSendOrderListDayOneDayBeforeDefault() + { + $this->prepareSaturdayThursdayConfig(); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 5, + ] + ), + 'currentDay' => '2022-08-26', + 'result' => '2022-09-08', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekWithSendOrderListDayOneDayBeforeDefault() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 2, + ] + ), + 'currentDay' => '2017-08-08', + 'result' => '2017-08-18', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekWithSendOrderListDayTwoDaysBeforeDefault() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 1 + ] + ), + 'currentDay' => '2020-04-05', + 'result' => '2020-04-10', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekWithSendOrderListDayTwoDaysBeforeDefaultAndChangedSendOrderListsDayDeltaAllowOrdersConfigOff() + { + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 3); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2021-02-05'), + 'delivery_rhythm_send_order_list_weekday' => 0, + ] + ), + 'currentDay' => '2021-08-01', + 'result' => '2021-08-20', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekWithSendOrderListDayTwoDaysBeforeDefaultAndChangedSendOrderListsDayDeltaAllowOrdersConfigOn() + { + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 3); + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2021-02-05'), + 'delivery_rhythm_send_order_list_weekday' => 0, + ] + ), + 'currentDay' => '2021-08-01', + 'result' => 'delivery-rhythm-triggered-delivery-break', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekWithSendOrderListDayMondayAllowOrdersConfigOff() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 1, + ] + ), + 'currentDay' => '2022-02-01', + 'result' => '2022-02-11', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1WeekWithSendOrderListDayMondayAllowOrdersConfigOn() + { + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 1, + ] + ), + 'currentDay' => '2022-02-01', + 'result' => 'delivery-rhythm-triggered-delivery-break', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekWithSendOrderListDayMondayAllowOrdersConfigOff() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 1, + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-01'), + ] + ), + 'currentDay' => '2019-02-25', + 'result' => '2019-03-15', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekWithSendOrderListDayMondayAllowOrdersConfigOn() + { + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 1, + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-01'), + ] + ), + 'currentDay' => '2019-02-25', + 'result' => 'delivery-rhythm-triggered-delivery-break', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekWithSendOrderListDayThursday() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 4, + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-01'), + ] + ), + 'currentDay' => '2019-03-08', + 'result' => '2019-03-15', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1MonthFirstFridayWithSendOrderListDaySunday() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + 'delivery_rhythm_send_order_list_weekday' => 1, + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2020-10-02'), + ] + ), + 'currentDay' => '2020-09-28', + 'result' => '2020-11-06', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekNotCurrentWeekAAllowOrderConfigOff() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-10'), + ] + ), + 'currentDay' => '2018-08-14', + 'result' => '2018-08-24', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekNotCurrentWeekAAllowOrderConfigOn() + { + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-10'), + ] + ), + 'currentDay' => '2018-08-14', + 'result' => 'delivery-rhythm-triggered-delivery-break', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekNotCurrentWeekBAllowOrderConfigOff() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-07-06'), + ] + ), + 'currentDay' => '2018-09-15', + 'result' => '2018-09-28', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekNotCurrentWeekBAllowOrderConfigOn() + { + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-07-06'), + ] + ), + 'currentDay' => '2018-09-15', + 'result' => 'delivery-rhythm-triggered-delivery-break', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekCurrentWeekAllowOrderConfigOff() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03'), + ] + ), + 'currentDay' => '2018-08-14', + 'result' => '2018-08-17', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekCurrentWeekAllowOrderConfigOn() + { + $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03'), + ] + ), + 'currentDay' => '2018-08-14', + 'result' => '2018-08-17', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2WeekD() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-22'), + ] + ), + 'currentDay' => '2019-03-15', + 'result' => '2019-03-22', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test4Week() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'week', + 'delivery_rhythm_count' => '4', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03'), + ] + ), + 'currentDay' => '2018-08-07', + 'result' => '2018-08-31', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test1MonthFirstWeekdayOfMonthA() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2017-08-07', + 'result' => '2017-09-01', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function testFirstWeekdayOfMonthB() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '1', + 'is_stock_product' => '1', + ] + ), + 'currentDay' => '2017-08-07', + 'result' => '2017-08-11', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function testLastMonthLastWeekdayOfMonthA() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '0', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2018-09-13', + 'result' => '2018-09-28', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function testLastMonthLastWeekdayOfMonthB() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '0', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2018-08-07', + 'result' => '2018-08-31', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function testLastMonthLastWeekdayOfMonthC() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2021-02-26'), + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '0', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2021-01-20', + 'result' => '2021-02-26', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test2Month() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '2', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2020-11-20', + 'result' => '2020-12-11', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test3Month() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '3', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2020-11-20', + 'result' => '2020-12-18', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function test4Month() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'month', + 'delivery_rhythm_count' => '4', + 'is_stock_product' => '0', + ] + ), + 'currentDay' => '2020-11-30', + 'result' => '2020-12-25', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + public function testIndividual() + { + $data = [ + 'product' => $this->Product->newEntity( + [ + 'delivery_rhythm_type' => 'individual', + 'delivery_rhythm_count' => '0', + 'is_stock_product' => '0', + 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03'), + ] + ), + 'currentDay' => '2017-08-07', + 'result' => '2018-08-03', + ]; + $this->assertGetNextPickupDayForProduct($data['product'], $data['currentDay'], $data['result']); + } + + private function assertGetNextPickupDayForProduct($product, $currentDay, $expectedResult) + { + $result = DeliveryRhythm::getNextPickupDayForProduct($product, $currentDay); + $this->assertEquals($expectedResult, $result); + } + + private function assertGetOrderPeriodFirstDay($currentDay, $expected) + { + $result = DeliveryRhythm::getOrderPeriodFirstDay(strtotime($currentDay)); + $this->assertEquals($expected, $result); + } + + private function assertGetOrderPeriodLastDay($currentDay, $expected) + { + $result = DeliveryRhythm::getOrderPeriodLastDay(strtotime($currentDay)); + $this->assertEquals($expected, $result); + } + + private function assertGetDeliveryDay($currentDay, $expected) + { + $result = DeliveryRhythm::getDeliveryDay(strtotime($currentDay)); + $result = date($this->MyTimeHelper->getI18Format('DateShortAlt'), $result); + $this->assertEquals($expected, $result); + } + + private function assertGetFormattedNextDeliveryDay($currentDay, $expected) + { + $result = DeliveryRhythm::getFormattedNextDeliveryDay(strtotime($currentDay)); + $this->assertEquals($expected, $result); + } + + private function assertGetOrderPeriodFirstDayByDeliveryDay($deliveryDay, $expected) + { + $result = DeliveryRhythm::getOrderPeriodFirstDayByDeliveryDay($deliveryDay, $expected); + $this->assertEquals($expected, $result); + } + + private function assertGetOrderPeriodLastDayByDeliveryDay($deliveryDay, $expected) + { + $result = DeliveryRhythm::getOrderPeriodLastDayByDeliveryDay($deliveryDay, $expected); + $this->assertEquals($expected, $result); + } + + private function assertGetLastOrderDay($product, $expected) + { + $result = DeliveryRhythm::getLastOrderDay( + $product['next_delivery_day'], + $product['delivery_rhythm_type'], + $product['delivery_rhythm_count'], + $product['delivery_rhythm_send_order_list_weekday'], + $product['delivery_rhythm_order_possible_until'], + ); + $this->assertEquals($expected, $result); + } + +} diff --git a/tests/TestCase/src/Lib/GlobalTest.php b/tests/TestCase/src/Lib/GlobalTest.php index 9dd3bab558..7f393277a1 100644 --- a/tests/TestCase/src/Lib/GlobalTest.php +++ b/tests/TestCase/src/Lib/GlobalTest.php @@ -1,4 +1,6 @@ markTestSkipped('The credentials for HelloCash are missing.'); } @@ -59,6 +63,16 @@ public function testGenerateReceipt() $invoice = $this->Invoice->find('all', [])->first(); + // not-owning user must not be able to download receipt + $this->loginAsCustomer(); + $this->get($this->Slug->getHelloCashReceipt($invoice->id)); + $this->assertAccessDeniedFlashMessage(); + + // owning user must be able to download receipt + $this->loginAsSuperadmin(); + $this->get($this->Slug->getHelloCashReceipt($invoice->id)); + $this->assertResponseCode(200); + $receiptHtml = $this->HelloCash->getReceipt($invoice->id, false); $this->assertRegExpWithUnquotedString('Beleg Nr.: ' . $invoice->invoice_number, $receiptHtml); @@ -195,7 +209,7 @@ public function testCancelInvoice() 'invoiceId' => $invoice->id, ] ); - $response = json_decode($this->_response); + $response = json_decode($this->_response->getBody()->__toString()); $this->runAndAssertQueue(); $invoice = $this->Invoice->find('all', [ diff --git a/tests/TestCase/src/Lib/OutputFilter/OutputFilterTest.php b/tests/TestCase/src/Lib/OutputFilter/OutputFilterTest.php new file mode 100644 index 0000000000..6f0dadca99 --- /dev/null +++ b/tests/TestCase/src/Lib/OutputFilter/OutputFilterTest.php @@ -0,0 +1,48 @@ + + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +use App\Lib\OutputFilter\OutputFilter; +use App\Test\TestCase\AppCakeTestCase; + +class OutputFilterTest extends AppCakeTestCase +{ + + /** + * @dataProvider protectEmailAdressesDataProvider + */ + public function testProtectEmailAdresses(string $input, int $count) + { + $result = OutputFilter::protectEmailAdresses($input); + preg_match_all('/javascript protected email address/', $result, $matches); + $this->assertEquals(count($matches[0]), $count); + } + + public function protectEmailAdressesDataProvider() + { + return [ + 'two-equal-emails-separated-with-space' => [ + 'test@test.com test@test.online', + 2, + ], + 'two-different-emails-separated-with-slash' => [ + 'test1@test.com/test2@test.com', + 2, + ], + ]; + } + +} diff --git a/tests/TestCase/src/Model/Table/CronjobsTableTest.php b/tests/TestCase/src/Model/Table/CronjobsTableTest.php index a79af2237a..05c8d12e6b 100644 --- a/tests/TestCase/src/Model/Table/CronjobsTableTest.php +++ b/tests/TestCase/src/Model/Table/CronjobsTableTest.php @@ -1,4 +1,5 @@ Cronjob = $this->getTableLocator()->get('Cronjobs'); } + public function testEditDailyValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'time_interval' => 'day', + 'day_of_month' => 4, + 'weekday' => 'Sunday', + 'not_before_time' => 'wrong-time', + ]); + $errors = $result->getErrors(); + $this->assertEquals('Beim Interval "täglich" bitte keinen Tag (Monat) angeben.', $errors['day_of_month']['time-interval-day-or-week-no-day-of-month']); + $this->assertEquals('Beim Interval "täglich" bitte keinen Wochentag angeben.', $errors['weekday']['time-interval-day-or-month-no-weekday']); + $this->assertEquals('Bitte gib eine gültige Uhrzeit ein.', $errors['not_before_time']['time']); + } + + public function testEditDailyOk() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'time_interval' => 'day', + 'day_of_month' => '', + 'weekday' => '', + 'not_before_time' => '18:00:00', + ]); + $this->assertEquals(false, $result->hasErrors()); + } + + public function testEditWeeklyValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'time_interval' => 'week', + 'day_of_month' => '2', + 'weekday' => '', + ]); + $errors = $result->getErrors(); + $this->assertEquals('Bitte wähle einen Wochentag aus.', $errors['weekday']['_empty']); + $this->assertEquals('Beim Interval "wöchentlich" bitte keinen Tag (Monat) angeben.', $errors['day_of_month']['time-interval-day-or-week-no-day-of-month']); + } + + public function testEditWeeklyOk() { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'time_interval' => 'week', + 'day_of_month' => '', + 'weekday' => 'Sunday', + ]); + $this->assertEquals(false, $result->hasErrors()); + } + + public function testPickupReminderValidation() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'time_interval' => 'week', + 'day_of_month' => '2', + 'weekday' => '', + ]); + $errors = $result->getErrors(); + $this->assertEquals('Bitte wähle einen Wochentag aus.', $errors['weekday']['_empty']); + $this->assertEquals('Beim Interval "wöchentlich" bitte keinen Tag (Monat) angeben.', $errors['day_of_month']['time-interval-day-or-week-no-day-of-month']); + } + + public function testEditPickupReminderValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'name' => 'PickupReminder', + 'time_interval' => 'month', + 'day_of_month' => '', + 'weekday' => 'Sunday', + ], + ['validate' => 'PickupReminder'], + ); + $errors = $result->getErrors(); + $this->assertEquals('Das Intervall muss "wöchentlich" sein.', $errors['time_interval']['equals']); + } + + public function testEditEmailOrderReminderValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'name' => 'PickupReminder', + 'time_interval' => 'month', + 'day_of_month' => '', + 'weekday' => 'Sunday', + ], + ['validate' => 'EmailOrderReminder'], + ); + $errors = $result->getErrors(); + $this->assertEquals('Das Intervall muss "wöchentlich" sein.', $errors['time_interval']['equals']); + } + + public function testEditSendDeliveryNotesValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'name' => 'SendDeliveryNotes', + 'time_interval' => 'week', + 'day_of_month' => '', + 'weekday' => 'Sunday', + ], + ['validate' => 'SendDeliveryNotes'], + ); + $errors = $result->getErrors(); + $this->assertEquals('Das Intervall muss "monatlich" sein.', $errors['time_interval']['equals']); + } + + public function testEditSendInvoicesToManufacturersValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'name' => 'SendInvoicesToManufacturers', + 'time_interval' => 'day', + 'day_of_month' => '', + 'weekday' => '', + ], + ['validate' => 'SendInvoicesToManufacturers'], + ); + $errors = $result->getErrors(); + $this->assertEquals('Das Intervall muss "monatlich" sein.', $errors['time_interval']['equals']); + } + + public function testEditSendOrderListsValidations() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'name' => 'SendOrderLists', + 'time_interval' => 'week', + 'day_of_month' => '', + 'weekday' => '', + ], + ['validate' => 'SendOrderLists'], + ); + $errors = $result->getErrors(); + $this->assertEquals('Das Intervall muss "täglich" sein.', $errors['time_interval']['equals']); + } + + public function testEditMonthlyOk() + { + $entity = $this->Cronjob->get(1); + $result = $this->Cronjob->patchEntity($entity, [ + 'time_interval' => 'month', + 'day_of_month' => '2', + 'weekday' => '', + ]); + $this->assertEquals(false, $result->hasErrors()); + } + public function testRunSunday() { $time = '2018-10-21 23:00:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $executedCronjobs = $this->Cronjob->run(); $this->assertEquals($executedCronjobs[0]['created'], $time); @@ -42,7 +193,7 @@ public function testRunSunday() public function testRunMonday() { $time = '2018-10-22 23:00:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $executedCronjobs = $this->Cronjob->run(); $this->assertEquals(2, count($executedCronjobs)); $this->assertEquals($executedCronjobs[0]['time_interval'], 'day'); @@ -53,8 +204,7 @@ public function testRunMonday() public function testPreviousCronjobLogFailure() { $time = '2018-10-22 23:00:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); - $this->Cronjob->cronjobRunDay = strtotime($time); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $this->Cronjob->CronjobLogs->save( $this->Cronjob->CronjobLogs->newEntity( [ @@ -74,8 +224,7 @@ public function testPreviousCronjobLogFailure() public function testPreviousCronjobLogRunning() { $time = '2018-10-22 23:00:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); - $this->Cronjob->cronjobRunDay = strtotime($time); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $this->Cronjob->CronjobLogs->save( $this->Cronjob->CronjobLogs->newEntity( [ @@ -94,7 +243,7 @@ public function testPreviousCronjobLogRunning() public function testCronjobNotYetExecutedWithinTimeInterval() { $time = '2018-10-23 22:30:01'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $this->Cronjob->CronjobLogs->save( $this->Cronjob->CronjobLogs->newEntity( [ @@ -112,7 +261,7 @@ public function testCronjobNotYetExecutedWithinTimeInterval() public function testCronjobAlreadyExecutedWithinTimeInterval() { - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC('2018-10-23 22:29:59')->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC('2018-10-23 22:29:59')->toUnixString(); $this->Cronjob->CronjobLogs->save( $this->Cronjob->CronjobLogs->newEntity( [ @@ -129,7 +278,7 @@ public function testCronjobAlreadyExecutedWithinTimeInterval() public function testCronjobWithInvalidParameterException() { $time = '2018-10-23 22:31:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $this->Cronjob->save( $this->Cronjob->patchEntity( $this->Cronjob->get(1), @@ -144,31 +293,9 @@ public function testCronjobWithInvalidParameterException() $this->assertEquals($executedCronjobs[0]['created'], $time); } - /** - * SocketException are triggered when email could not be sent - * set cronjob success to 1 to avoid that it is called again - */ - public function testCronjobWithSocketException() - { - $time = '2018-10-23 22:31:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); - $this->Cronjob->save( - $this->Cronjob->patchEntity( - $this->Cronjob->get(1), - [ - 'name' => 'TestCronjobWithSocketException' - ], - ) - ); - $executedCronjobs = $this->Cronjob->run(); - $this->assertEquals(1, count($executedCronjobs)); - $this->assertEquals($executedCronjobs[0]['success'], 1); - $this->assertEquals($executedCronjobs[0]['created'], $time); - } - public function testCronjobAlreadyExecutedOnCurrentDay() { - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC('2018-10-25 22:30:02')->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC('2018-10-25 22:30:02')->toUnixString(); $this->Cronjob->CronjobLogs->save( $this->Cronjob->CronjobLogs->newEntity( [ @@ -184,7 +311,7 @@ public function testCronjobAlreadyExecutedOnCurrentDay() public function testRunMonthlyBeforeNotBeforeTime() { - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC('2018-10-11 07:29:00')->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC('2018-10-11 07:29:00')->toUnixString(); $this->Cronjob->save( $this->Cronjob->patchEntity( $this->Cronjob->get(1), @@ -200,7 +327,7 @@ public function testRunMonthlyBeforeNotBeforeTime() public function testRunMonthlyAfterNotBeforeTime() { $time = '2018-10-11 07:31:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $this->Cronjob->save( $this->Cronjob->patchEntity( $this->Cronjob->get(1), @@ -217,7 +344,7 @@ public function testRunMonthlyAfterNotBeforeTime() public function testRunMonthlyLastDayOfMonthAfterNotBeforeTime() { $time = '2018-11-30 07:31:00'; - $this->Cronjob->cronjobRunDay = $this->Time->getTimeObjectUTC($time)->toUnixString(); + $this->Cronjob->cronjobRunDay = (int) $this->Time->getTimeObjectUTC($time)->toUnixString(); $this->Cronjob->updateAll( [ 'active' => APP_OFF, @@ -252,7 +379,7 @@ public function testInvalidWeekday() $this->expectExceptionMessage('weekday not available'); $executedCronjobs = $this->Cronjob->run(); $this->assertEquals(0, count($executedCronjobs)); - $this->assertEmpty(0, $this->CronjobLogs->find('all')->all()); + $this->assertEmpty(0, $this->Cronjob->CronjobLogs->find('all')->all()); } public function testInvalidDayOfMonth() @@ -268,7 +395,7 @@ public function testInvalidDayOfMonth() $this->expectException(InvalidParameterException::class); $this->expectExceptionMessage('day of month not available or not valid'); $this->Cronjob->run(); - $this->assertEmpty(0, $this->CronjobLogs->find('all')->all()); + $this->assertEmpty(0, $this->Cronjob->CronjobLogs->find('all')->all()); } } \ No newline at end of file diff --git a/tests/TestCase/src/View/Helper/MyHtmlHelperTest.php b/tests/TestCase/src/View/Helper/MyHtmlHelperTest.php index bfb484b47a..796e60b5ae 100644 --- a/tests/TestCase/src/View/Helper/MyHtmlHelperTest.php +++ b/tests/TestCase/src/View/Helper/MyHtmlHelperTest.php @@ -1,4 +1,6 @@ MyHtmlHelper = new MyHtmlHelper(new View()); } - public function testRemoveTimestampFromFileValidTimestamp() + public function testAnonymizeCustomerNameNormal() { - $filename = 'asdf.jpg?1539847477'; - $result = 'asdf.jpg'; - $this->assertEquals($result, $this->MyHtmlHelper->removeTimestampFromFile($filename)); + $name = 'Demo Admin'; + $id = 1; + $result = 'D.A. - ID 1'; + $this->assertEquals($result, $this->MyHtmlHelper->anonymizeCustomerName($name, $id)); } - public function testRemoveTimestampFromFileNoTimestamp() + public function testAnonymizeCustomerNameAdvanced() + { + $name = 'Demo-Marie Test Admin'; + $id = 1; + $result = 'D.T.A. - ID 1'; + $this->assertEquals($result, $this->MyHtmlHelper->anonymizeCustomerName($name, $id)); + } + + /** + * @dataProvider removeTimestampFromFileDataProvider + */ + public function testRemoveTimestampFromFile(string $filename, string $result): void { - $filename = 'asdf.jpg'; - $result = 'asdf.jpg'; $this->assertEquals($result, $this->MyHtmlHelper->removeTimestampFromFile($filename)); } - public function testRemoveTimestampFromFileInvalidTimestamp() + public function removeTimestampFromFileDataProvider() { - $filename = 'asdf.jpg?adfs'; - $result = 'asdf.jpg'; - $this->assertEquals($result, $this->MyHtmlHelper->removeTimestampFromFile($filename)); + return [ + 'correct-timestamp' => [ + 'asdf.jpg?1539847477', + 'asdf.jpg', + ], + 'no-timestamp' => [ + 'asdf.jpg', + 'asdf.jpg', + ], + 'invalid-timestamp' => [ + 'asdf.jpg?adfs', + 'asdf.jpg', + ], + ]; } } diff --git a/tests/TestCase/src/View/Helper/MyNumberHelperTest.php b/tests/TestCase/src/View/Helper/MyNumberHelperTest.php index 2c16700b2f..4132f5e8f8 100644 --- a/tests/TestCase/src/View/Helper/MyNumberHelperTest.php +++ b/tests/TestCase/src/View/Helper/MyNumberHelperTest.php @@ -1,4 +1,6 @@ MyNumberHelper = new MyNumberHelper(new View()); diff --git a/tests/TestCase/src/View/Helper/MyTimeHelperTest.php b/tests/TestCase/src/View/Helper/MyTimeHelperTest.php index b8fb825ad7..85dfa2cb1b 100644 --- a/tests/TestCase/src/View/Helper/MyTimeHelperTest.php +++ b/tests/TestCase/src/View/Helper/MyTimeHelperTest.php @@ -1,4 +1,6 @@ assertEquals($result, '2018-06-12'); } - private function prepareThursdayFridayConfig() - { - $this->changeReadOnlyConfiguration('FCS_WEEKLY_PICKUP_DAY', 5); - $this->changeReadOnlyConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 1); - } - - private function prepareWednesdayFridayConfig() - { - $this->changeReadOnlyConfiguration('FCS_WEEKLY_PICKUP_DAY', 5); - $this->changeReadOnlyConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 2); - } - - private function prepareTuesdayFridayConfig() - { - $this->changeReadOnlyConfiguration('FCS_WEEKLY_PICKUP_DAY', 5); - $this->changeReadOnlyConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 3); - } - - private function prepareMondayTuesdayConfig() - { - $this->changeReadOnlyConfiguration('FCS_WEEKLY_PICKUP_DAY', 2); - $this->changeReadOnlyConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 1); - } - - public function testGetFormattedNextDeliveryDayThursdayFriday() - { - $this->prepareThursdayFridayConfig(); - $this->assertGetFormattedNextDeliveryDay('08.10.2018', '12.10.2018'); // monday - $this->assertGetFormattedNextDeliveryDay('09.10.2018', '12.10.2018'); // tuesday - $this->assertGetFormattedNextDeliveryDay('10.10.2018', '12.10.2018'); // wednesday - $this->assertGetFormattedNextDeliveryDay('11.10.2018', '12.10.2018'); // thursday - $this->assertGetFormattedNextDeliveryDay('12.10.2018', '12.10.2018'); // friday - $this->assertGetFormattedNextDeliveryDay('13.10.2018', '19.10.2018'); // saturday - $this->assertGetFormattedNextDeliveryDay('14.10.2018', '19.10.2018'); // sunday - $this->assertGetFormattedNextDeliveryDay('15.10.2018', '19.10.2018'); // monday - $this->assertGetFormattedNextDeliveryDay('16.10.2018', '19.10.2018'); // tuesday - } - - public function testGetFormattedNextDeliveryDayWednesdayFriday() - { - $this->prepareWednesdayFridayConfig(); - $this->assertGetFormattedNextDeliveryDay('08.10.2018', '12.10.2018'); // monday - $this->assertGetFormattedNextDeliveryDay('09.10.2018', '12.10.2018'); // tuesday - $this->assertGetFormattedNextDeliveryDay('10.10.2018', '12.10.2018'); // wednesday - $this->assertGetFormattedNextDeliveryDay('11.10.2018', '12.10.2018'); // thursday - $this->assertGetFormattedNextDeliveryDay('12.10.2018', '12.10.2018'); // friday - $this->assertGetFormattedNextDeliveryDay('13.10.2018', '19.10.2018'); // saturday - $this->assertGetFormattedNextDeliveryDay('14.10.2018', '19.10.2018'); // sunday - $this->assertGetFormattedNextDeliveryDay('15.10.2018', '19.10.2018'); // monday - $this->assertGetFormattedNextDeliveryDay('16.10.2018', '19.10.2018'); // tuesday - } - - public function testGetOrderPeriodFirstDayThursdayFriday() - { - $this->prepareThursdayFridayConfig(); - $this->assertGetOrderPeriodFirstDay('27.11.2017', '23.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('28.11.2017', '23.11.2017'); // tuesday - $this->assertGetOrderPeriodFirstDay('29.11.2017', '23.11.2017'); // wednesday - $this->assertGetOrderPeriodFirstDay('30.11.2017', '23.11.2017'); // thursday - $this->assertGetOrderPeriodFirstDay('01.12.2017', '23.11.2017'); // friday - $this->assertGetOrderPeriodFirstDay('02.12.2017', '30.11.2017'); // saturday - $this->assertGetOrderPeriodFirstDay('03.12.2017', '30.11.2017'); // sunday - $this->assertGetOrderPeriodFirstDay('04.12.2017', '30.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('05.12.2017', '30.11.2017'); // tuesday - } - - public function testGetOrderPeriodFirstDayWednesdayFriday() - { - $this->prepareWednesdayFridayConfig(); - $this->assertGetOrderPeriodFirstDay('27.11.2017', '22.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('28.11.2017', '22.11.2017'); // tuesday - $this->assertGetOrderPeriodFirstDay('29.11.2017', '22.11.2017'); // wednesday - $this->assertGetOrderPeriodFirstDay('30.11.2017', '22.11.2017'); // thursday - $this->assertGetOrderPeriodFirstDay('01.12.2017', '22.11.2017'); // friday - $this->assertGetOrderPeriodFirstDay('02.12.2017', '29.11.2017'); // saturday - $this->assertGetOrderPeriodFirstDay('03.12.2017', '29.11.2017'); // sunday - $this->assertGetOrderPeriodFirstDay('04.12.2017', '29.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('05.12.2017', '29.11.2017'); // tuesday - } - - public function testGetOrderPeriodFirstDayTuesdayFriday() - { - $this->prepareTuesdayFridayConfig(); - $this->assertGetOrderPeriodFirstDay('27.11.2017', '21.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('28.11.2017', '21.11.2017'); // tuesday - $this->assertGetOrderPeriodFirstDay('29.11.2017', '21.11.2017'); // wednesday - $this->assertGetOrderPeriodFirstDay('30.11.2017', '21.11.2017'); // thursday - $this->assertGetOrderPeriodFirstDay('01.12.2017', '21.11.2017'); // friday - $this->assertGetOrderPeriodFirstDay('02.12.2017', '28.11.2017'); // saturday - $this->assertGetOrderPeriodFirstDay('03.12.2017', '28.11.2017'); // sunday - $this->assertGetOrderPeriodFirstDay('04.12.2017', '28.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('05.12.2017', '28.11.2017'); // tuesday - } - - public function testGetOrderPeriodFirstDayMondayTuesday() - { - $this->prepareMondayTuesdayConfig(); - $this->assertGetOrderPeriodFirstDay('27.11.2017', '20.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('28.11.2017', '20.11.2017'); // tuesday - $this->assertGetOrderPeriodFirstDay('29.11.2017', '27.11.2017'); // wednesday - $this->assertGetOrderPeriodFirstDay('30.11.2017', '27.11.2017'); // thursday - $this->assertGetOrderPeriodFirstDay('01.12.2017', '27.11.2017'); // friday - $this->assertGetOrderPeriodFirstDay('02.12.2017', '27.11.2017'); // saturday - $this->assertGetOrderPeriodFirstDay('03.12.2017', '27.11.2017'); // sunday - $this->assertGetOrderPeriodFirstDay('04.12.2017', '27.11.2017'); // monday - $this->assertGetOrderPeriodFirstDay('05.12.2017', '27.11.2017'); // tuesday - } - - public function testGetOrderPeriodLastDayThursdayFriday() - { - $this->prepareThursdayFridayConfig(); - $this->assertGetOrderPeriodLastDay('27.11.2017', '29.11.2017'); // monday - $this->assertGetOrderPeriodLastDay('28.11.2017', '29.11.2017'); // tuesday - $this->assertGetOrderPeriodLastDay('29.11.2017', '29.11.2017'); // wednesday - $this->assertGetOrderPeriodLastDay('30.11.2017', '29.11.2017'); // thursday - $this->assertGetOrderPeriodLastDay('01.12.2017', '29.11.2017'); // friday - $this->assertGetOrderPeriodLastDay('02.12.2017', '06.12.2017'); // saturday - $this->assertGetOrderPeriodLastDay('03.12.2017', '06.12.2017'); // sunday - $this->assertGetOrderPeriodLastDay('04.12.2017', '06.12.2017'); // monday - $this->assertGetOrderPeriodLastDay('05.12.2017', '06.12.2017'); // tuesday - } - - public function testGetOrderPeriodLastDayWednesdayFriday() - { - $this->prepareWednesdayFridayConfig(); - $this->assertGetOrderPeriodLastDay('27.11.2017', '28.11.2017'); // monday - $this->assertGetOrderPeriodLastDay('28.11.2017', '28.11.2017'); // tuesday - $this->assertGetOrderPeriodLastDay('29.11.2017', '28.11.2017'); // wednesday - $this->assertGetOrderPeriodLastDay('30.11.2017', '28.11.2017'); // thursday - $this->assertGetOrderPeriodLastDay('01.12.2017', '28.11.2017'); // friday - $this->assertGetOrderPeriodLastDay('02.12.2017', '05.12.2017'); // saturday - $this->assertGetOrderPeriodLastDay('03.12.2017', '05.12.2017'); // sunday - $this->assertGetOrderPeriodLastDay('04.12.2017', '05.12.2017'); // monday - $this->assertGetOrderPeriodLastDay('05.12.2017', '05.12.2017'); // tuesday - } - - public function testGetOrderPeriodLastDayTuesdayFriday() - { - $this->prepareTuesdayFridayConfig(); - $this->assertGetOrderPeriodLastDay('27.11.2017', '27.11.2017'); // monday - $this->assertGetOrderPeriodLastDay('28.11.2017', '27.11.2017'); // tuesday - $this->assertGetOrderPeriodLastDay('29.11.2017', '27.11.2017'); // wednesday - $this->assertGetOrderPeriodLastDay('30.11.2017', '27.11.2017'); // thursday - $this->assertGetOrderPeriodLastDay('01.12.2017', '27.11.2017'); // friday - $this->assertGetOrderPeriodLastDay('02.12.2017', '04.12.2017'); // saturday - $this->assertGetOrderPeriodLastDay('03.12.2017', '04.12.2017'); // sunday - $this->assertGetOrderPeriodLastDay('04.12.2017', '04.12.2017'); // monday - $this->assertGetOrderPeriodLastDay('05.12.2017', '04.12.2017'); // tuesday - } - - public function testGetOrderPeriodLastDayMondayTuesday() - { - $this->prepareMondayTuesdayConfig(); - $this->assertGetOrderPeriodLastDay('27.11.2017', '26.11.2017'); // monday - $this->assertGetOrderPeriodLastDay('28.11.2017', '26.11.2017'); // tuesday - $this->assertGetOrderPeriodLastDay('29.11.2017', '03.12.2017'); // wednesday - $this->assertGetOrderPeriodLastDay('30.11.2017', '03.12.2017'); // thursday - $this->assertGetOrderPeriodLastDay('01.12.2017', '03.12.2017'); // friday - $this->assertGetOrderPeriodLastDay('02.12.2017', '03.12.2017'); // saturday - $this->assertGetOrderPeriodLastDay('03.12.2017', '03.12.2017'); // sunday - $this->assertGetOrderPeriodLastDay('04.12.2017', '03.12.2017'); // monday - $this->assertGetOrderPeriodLastDay('05.12.2017', '03.12.2017'); // tuesday - $this->assertGetOrderPeriodLastDay('06.12.2017', '10.12.2017'); // wednesday - } - - public function testGetDeliveryDayTuesdayFriday() - { - $this->prepareTuesdayFridayConfig(); - $this->assertGetDeliveryDay('25.07.2018', '03.08.2018'); // wednesday - } - public function testGetLastDayOfLastMonth() { $this->assertGetLastDayOfLastMonth('2018-03-11', '28.02.2018'); @@ -221,78 +57,6 @@ public function testGetFirstDayOfLastMonth() $this->assertGetFirstDayOfLastMonth('2018-01-11', '01.12.2017'); } - public function testGetLastOrderDayWeeklySendOrderListsDayNormal() - { - $product = [ - 'next_delivery_day' => '2020-12-04', - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => 1, - 'delivery_rhythm_send_order_list_weekday' => 3, - 'delivery_rhythm_order_possible_until' => null, - ]; - $this->assertGetLastOrderDay($product, '2020-12-01'); - } - - public function testGetLastOrderDayWeeklySendOrderListsDayMonday() - { - $product = [ - 'next_delivery_day' => '2020-12-04', - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => 1, - 'delivery_rhythm_send_order_list_weekday' => 2, - 'delivery_rhythm_order_possible_until' => null, - ]; - $this->assertGetLastOrderDay($product, '2020-11-30'); - } - - public function testGetLastOrderDayMonthlySendOrderListsDayNormal() - { - $product = [ - 'next_delivery_day' => '2020-12-25', - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => 0, - 'delivery_rhythm_send_order_list_weekday' => 3, - 'delivery_rhythm_order_possible_until' => null, - ]; - $this->assertGetLastOrderDay($product, '2020-12-22'); - } - - public function testGetLastOrderDayMonthlySendOrderListsDaySunday() - { - $product = [ - 'next_delivery_day' => '2020-12-25', - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => 0, - 'delivery_rhythm_send_order_list_weekday' => 1, - 'delivery_rhythm_order_possible_until' => null, - ]; - $this->assertGetLastOrderDay($product, '2020-12-20'); - } - - public function testGetLastOrderDayIndividual() - { - $product = [ - 'next_delivery_day' => '2020-12-25', - 'delivery_rhythm_type' => 'individual', - 'delivery_rhythm_count' => 0, - 'delivery_rhythm_send_order_list_weekday' => 3, - 'delivery_rhythm_order_possible_until' => new FrozenDate('2020-12-12'), - ]; - $this->assertGetLastOrderDay($product, '2020-12-12'); - } - - private function assertGetLastOrderDay($product, $expected) - { - $result = $this->MyTimeHelper->getLastOrderDay( - $product['next_delivery_day'], - $product['delivery_rhythm_type'], - $product['delivery_rhythm_count'], - $product['delivery_rhythm_send_order_list_weekday'], - $product['delivery_rhythm_order_possible_until'], - ); - $this->assertEquals($expected, $result); - } - private function assertGetLastDayOfLastMonth($currentDay, $expected) { $result = $this->MyTimeHelper->getLastDayOfLastMonth($currentDay); @@ -305,29 +69,4 @@ private function assertGetFirstDayOfLastMonth($currentDay, $expected) $this->assertEquals($expected, $result); } - private function assertGetDeliveryDay($currentDay, $expected) - { - $result = $this->MyTimeHelper->getDeliveryDay(strtotime($currentDay)); - $result = date($this->MyTimeHelper->getI18Format('DateShortAlt'), $result); - $this->assertEquals($expected, $result); - } - - private function assertGetOrderPeriodFirstDay($currentDay, $expected) - { - $result = $this->MyTimeHelper->getOrderPeriodFirstDay(strtotime($currentDay)); - $this->assertEquals($expected, $result); - } - - private function assertGetOrderPeriodLastDay($currentDay, $expected) - { - $result = $this->MyTimeHelper->getOrderPeriodLastDay(strtotime($currentDay)); - $this->assertEquals($expected, $result); - } - - private function assertGetFormattedNextDeliveryDay($currentDay, $expected) - { - $result = $this->MyTimeHelper->getFormattedNextDeliveryDay(strtotime($currentDay)); - $this->assertEquals($expected, $result); - } - } diff --git a/tests/TestCase/src/View/Helper/PricePerUnitHelperTest.php b/tests/TestCase/src/View/Helper/PricePerUnitHelperTest.php index 20b65951cc..f5ff5aabae 100644 --- a/tests/TestCase/src/View/Helper/PricePerUnitHelperTest.php +++ b/tests/TestCase/src/View/Helper/PricePerUnitHelperTest.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com */ use Cake\Core\Configure; use Cake\Utility\Security; +use Migrations\Migrations; +use Migrations\TestSuite\Migrator; require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/config/bootstrap.php'; +// 1) import structure +$migrator = new Migrator(); +$migrator->runMany([ + ['plugin' => 'Queue', 'connection' => 'test'], +]); + +// 2) run new migrations (located in main folder) +//$migrator->run([], false); // causes "Going to drop all tables in this source, and re-apply migrations." +$migrations = new Migrations(); +$migrations->migrate(['connection' => 'test']); + +// 3) add test data (generated to fit after run migrations in init folder) +$migrations->seed([ + 'connection' => 'test', + 'source' => 'Seeds' . DS . 'tests', // needs to be a subfolder of config +]); + +require dirname(__DIR__) . '/config/bootstrap_locale.php'; + Security::setSalt(Configure::read('Security.salt_for_unit_tests')); // always set to app.customerMainNamePart to firstname for unit tests even if different in custom_config.php diff --git a/tests/config/data/customerInvoiceWithTaxBasedOnInvoiceSum.html b/tests/config/data/customerInvoiceWithTaxBasedOnInvoiceSum.html index b28d9648c7..5fad57f46d 100644 --- a/tests/config/data/customerInvoiceWithTaxBasedOnInvoiceSum.html +++ b/tests/config/data/customerInvoiceWithTaxBasedOnInvoiceSum.html @@ -1 +1 @@ -


FoodCoop Test
Demostraße 4
A-4564 Demostadt
demo-foodcoop@maillinator.com

Rechnung

'; // roughly 85,60mm x 53,98mm + $pdf->table .= ''; // roughly 85,60mm x 53,98mm $pdf->table .= ''; // START ROW logo and name block @@ -40,6 +42,7 @@ // START ROW barcode and product image $pdf->table .= ''; + $pdf->table .= ''; //spacer left of barcode $pdf->table .= ''; $pdf->table .= ''; //spacer between barcode and customer image // move user image to bottom - $pdf->table .= ''); // check downloadable file as correct manufacturer - $this->get($orderListDownloadUrl); + $this->get($orderListDownloadUrlAnonymized); $this->assertResponseOk(); $this->assertContentType('pdf'); + // check if clear text file is not downloadable with anonymized configuration + $this->get($orderListDownloadUrlClearText); + $this->assertResponseCode(401); + + // check if anonymized file is not downloadable with clear text configuration + $this->changeManufacturer(4, 'anonymize_customers', 0); + $this->get($orderListDownloadUrlAnonymized); + $this->assertResponseCode(401); + // check downloadable file as wrong manufacturer $this->loginAsVegetableManufacturer(); - $this->get($orderListDownloadUrl); + $this->get($orderListDownloadUrlClearText); $this->assertResponseCode(401); // check downloadable file as admin $this->loginAsAdmin(); - $this->get($orderListDownloadUrl); + $this->get($orderListDownloadUrlClearText); + $this->assertResponseOk(); + $this->assertContentType('pdf'); + + $this->get($orderListDownloadUrlAnonymized); $this->assertResponseOk(); $this->assertContentType('pdf'); // check list page as admin $this->get($listPageUrl); - $this->assertResponseContains('3 Datensätze'); + $this->assertResponseContains('4 Datensätze'); $this->assertResponseContains(''); $this->assertResponseContains(''); $this->assertResponseContains(''); diff --git a/plugins/Admin/tests/TestCase/src/Controller/ManufacturersControllerTest.php b/plugins/Admin/tests/TestCase/src/Controller/ManufacturersControllerTest.php index 642b74173c..1f2b1e4740 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/ManufacturersControllerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/ManufacturersControllerTest.php @@ -1,4 +1,6 @@ [ @@ -216,26 +220,19 @@ public function testEditOptionsNoDeliveryDays() $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); $this->Product = $this->getTableLocator()->get('Products'); - $query = 'UPDATE ' . $this->OrderDetail->getTable().' SET pickup_day = :pickupDay WHERE id_order_detail IN(1);'; - $params = [ - 'pickupDay' => $noDeliveryDayA, - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); - - $query = 'UPDATE ' . $this->OrderDetail->getTable().' SET pickup_day = :pickupDay WHERE id_order_detail IN (2,3);'; - $params = [ - 'pickupDay' => $noDeliveryDayB, - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); - - $query = 'UPDATE ' . $this->Product->getTable().' SET id_manufacturer = :manufacturerId;'; - $params = [ - 'manufacturerId' => $manufacturerId, - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $orderDetailEntityA = $this->OrderDetail->get(1); + $orderDetailEntityA->pickup_day = $noDeliveryDayA; + $this->OrderDetail->save($orderDetailEntityA); + + $orderDetailEntityB = $this->OrderDetail->get(2); + $orderDetailEntityB->pickup_day = $noDeliveryDayB; + $this->OrderDetail->save($orderDetailEntityB); + + $orderDetailEntityC = $this->OrderDetail->get(3); + $orderDetailEntityC->pickup_day = $noDeliveryDayB; + $this->OrderDetail->save($orderDetailEntityC); + + $this->Product->updateAll(['id_manufacturer' => $manufacturerId], []); $this->post( $this->Slug->getManufacturerEditOptions($manufacturerId), @@ -350,7 +347,7 @@ public function testEditMain() $this->logout(); } - private function doTestCustomerRecord($manufacturer) + private function doTestCustomerRecord($manufacturer): void { $customerRecord = $this->Manufacturer->getCustomerRecord($manufacturer->address_manufacturer->email); $this->assertEquals($manufacturer->address_manufacturer->firstname, $customerRecord->firstname); @@ -359,12 +356,7 @@ private function doTestCustomerRecord($manufacturer) $this->assertEquals(APP_ON, $customerRecord->active); } - /** - * - * @param array $data - * @return string - */ - private function add($data) + private function add($data): void { $this->post($this->Slug->getManufacturerAdd(), $data); } diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerAddFeedbackTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerAddFeedbackTest.php index 111a7a28a1..bd020f577d 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerAddFeedbackTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerAddFeedbackTest.php @@ -1,4 +1,6 @@ great! Thank you!'; public $orderDetailId = 1; @@ -47,6 +50,7 @@ public function testAddFeedbackWithEmptyFeedback() public function testAddFeedbackAsSuperadmin() { + $this->changeManufacturer(5, 'anonymize_customers', 1); $this->loginAsSuperadmin(); $this->addFeedbackToOrderDetail($this->orderDetailId, $this->orderDetailFeedback); $this->assertJsonOk(); diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerCancellationTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerCancellationTest.php index 9988e1f2ce..dae866736e 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerCancellationTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerCancellationTest.php @@ -1,4 +1,6 @@ deleteAndAssertRemoveFromDatabase([$this->orderDetailIdA]); $expectedToEmails = [Configure::read('test.loginEmailSuperadmin')]; - $expectedCcEmails = []; - $this->assertOrderDetailDeletedEmails(0, $expectedToEmails, $expectedCcEmails); + $this->assertOrderDetailDeletedEmails(0, $expectedToEmails); $this->assertChangedStockAvailable($this->productIdA, 98); } @@ -57,24 +60,26 @@ public function testCancellationAsSuperadminWithEnabledNotification() $this->deleteAndAssertRemoveFromDatabase([$this->orderDetailIdA]); $expectedToEmails = [Configure::read('test.loginEmailSuperadmin')]; - $expectedCcEmails = []; - $this->assertOrderDetailDeletedEmails(0, $expectedToEmails, $expectedCcEmails); + $this->assertOrderDetailDeletedEmails(0, $expectedToEmails); $this->assertChangedStockAvailable($this->productIdA, 98); } public function testCancellationAsSuperadminWithEnabledNotificationAfterOrderListsWereSent() { + $this->changeManufacturer(5, 'anonymize_customers', 1); $this->loginAsSuperadmin(); $this->simulateSendOrderListsCronjob($this->orderDetailIdA); $this->deleteAndAssertRemoveFromDatabase([$this->orderDetailIdA]); $expectedToEmails = [Configure::read('test.loginEmailSuperadmin')]; - $expectedCcEmails = [ - Configure::read('test.loginEmailVegetableManufacturer') - ]; - $this->assertOrderDetailDeletedEmails(0, $expectedToEmails, $expectedCcEmails); + $this->assertOrderDetailDeletedEmails(0, $expectedToEmails); + + $expectedToEmails = [Configure::read('test.loginEmailVegetableManufacturer')]; + $this->assertOrderDetailDeletedEmails(1, $expectedToEmails); + $this->assertMailContainsHtmlAt(1, 'Hallo Demo Gemüse-Hersteller'); + $this->assertMailContainsHtmlAt(1, 'D.S. - ID 92'); $this->assertChangedStockAvailable($this->productIdA, 98); } @@ -90,8 +95,7 @@ public function testCancellationAsSuperadminWithDisabledNotification() $this->deleteAndAssertRemoveFromDatabase([$this->orderDetailIdA]); $expectedToEmails = [Configure::read('test.loginEmailSuperadmin')]; - $expectedCcEmails = []; - $this->assertOrderDetailDeletedEmails(0, $expectedToEmails, $expectedCcEmails); + $this->assertOrderDetailDeletedEmails(0, $expectedToEmails); $this->assertChangedStockAvailable($this->productIdA, 98); } @@ -160,7 +164,7 @@ private function deleteAndAssertRemoveFromDatabase($orderDetailIds) $this->assertEmpty($orderDetails); } - private function assertOrderDetailDeletedEmails($emailIndex, $expectedToEmails, $expectedCcEmails) + private function assertOrderDetailDeletedEmails($emailIndex, $expectedToEmails) { $this->runAndAssertQueue(); @@ -170,9 +174,6 @@ private function assertOrderDetailDeletedEmails($emailIndex, $expectedToEmails, foreach($expectedToEmails as $expectedToEmail) { $this->assertMailSentToAt($emailIndex, $expectedToEmail); } - foreach($expectedCcEmails as $expectedCcEmail) { - $this->assertMailSentWithAt($emailIndex, $expectedCcEmail, 'cc'); - } $this->assertMailContainsHtmlAt($emailIndex, $this->cancellationReason); $this->assertMailContainsHtmlAt($emailIndex, '1,82'); diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditAmountTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditAmountTest.php index 63c2dc488d..ccf708286e 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditAmountTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditAmountTest.php @@ -1,4 +1,6 @@ changeManufacturer(5, 'anonymize_customers', 1); $this->loginAsSuperadmin(); $this->mockCart = $this->generateAndGetCart(1, 2); $orderDetailId = $this->mockCart->cart_products[0]->order_detail->id_order_detail; @@ -125,8 +130,12 @@ public function testEditOrderDetailAmountAsSuperadminWithEnabledNotificationAfte $this->assertEquals(10, $changedOrder->cart_products[0]->order_detail->tax_rate); $expectedToEmail = Configure::read('test.loginEmailSuperadmin'); - $expectedCcEmail = Configure::read('test.loginEmailVegetableManufacturer'); - $this->assertOrderDetailProductAmountChangedEmails(1, $expectedToEmail, $expectedCcEmail); + $this->assertOrderDetailProductAmountChangedEmails(1, $expectedToEmail); + + $expectedToEmail = Configure::read('test.loginEmailVegetableManufacturer'); + $this->assertOrderDetailProductAmountChangedEmails(2, $expectedToEmail); + $this->assertMailContainsHtmlAt(2, 'Hallo Demo Gemüse-Hersteller'); + $this->assertMailContainsHtmlAt(2, 'D.S. - ID 92'); $this->assertChangedStockAvailable($this->productIdA, 96); } @@ -152,7 +161,7 @@ public function testEditOrderDetailAmountAsSuperadminWithDisabledNotification() $this->assertChangedStockAvailable($this->productIdA, 96); } - private function assertOrderDetailProductAmountChangedEmails($emailIndex, $expectedToEmail, $expectedCcEmail = null) + private function assertOrderDetailProductAmountChangedEmails($emailIndex, $expectedToEmail) { $this->runAndAssertQueue(); $this->assertMailSubjectContainsAt($emailIndex, 'Bestellte Anzahl angepasst: Artischocke : Stück'); @@ -161,9 +170,6 @@ private function assertOrderDetailProductAmountChangedEmails($emailIndex, $expec $this->assertMailContainsHtmlAt($emailIndex, 'Neue Anzahl: ' . $this->newAmount . ''); $this->assertMailContainsHtmlAt($emailIndex, 'Demo Gemüse-Hersteller'); $this->assertMailSentToAt($emailIndex, $expectedToEmail); - if ($expectedCcEmail !== null) { - $this->assertMailSentWithAt($emailIndex, $expectedCcEmail, 'cc'); - } } private function editOrderDetailAmount($orderDetailId, $productAmount, $editAmountReason) diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditCustomerTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditCustomerTest.php index 06ccac2542..a2e53ac934 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditCustomerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditCustomerTest.php @@ -1,4 +1,6 @@ loginAsVegetableManufacturer(); - $this->editOrderDetailCustomer($this->orderDetailIdA, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount); + $this->editOrderDetailCustomer($this->orderDetailIdA, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount, true); $this->assertNotPerfectlyImplementedAccessRestricted(); } public function testEditOrderDetailCustomerAsSuperadminNotParted() { $this->loginAsSuperadmin(); - $this->editOrderDetailCustomer($this->orderDetailIdA, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount); + $this->editOrderDetailCustomer($this->orderDetailIdA, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount, true); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$this->orderDetailIdA]); $this->assertEquals($this->newCustomerId, $changedOrderDetails[0]->id_customer); $this->assertEquals($this->editCustomerAmount, $changedOrderDetails[0]->product_amount); @@ -63,7 +66,7 @@ public function testEditOrderDetailCustomerAsSuperadminPartedIn2And5WithUnits() $cart = $this->getCartById($cartId); $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailCustomer($orderDetailId, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount); + $this->editOrderDetailCustomer($orderDetailId, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount, true); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId, 5]); $this->assertEquals(Configure::read('test.superadminId'), $changedOrderDetails[0]->id_customer); @@ -99,7 +102,7 @@ public function testEditOrderDetailCustomerAsSuperadminPartedIn2And5WithUnitsAnd $cart = $this->getCartById($cartId); $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailCustomer($orderDetailId, $this->newCustomerId, $this->editCustomerReason, 3); + $this->editOrderDetailCustomer($orderDetailId, $this->newCustomerId, $this->editCustomerReason, 3, true); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId, 5]); $changedOrderDetails = $this->OrderDetail->find('all', [ @@ -143,7 +146,7 @@ public function testEditOrderDetailCustomerAsSuperadminPartedIn2And5() $cart = $this->getCartById($cartId); $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailCustomer($orderDetailId, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount); + $this->editOrderDetailCustomer($orderDetailId, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount, true); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId, 5]); $this->assertEquals(Configure::read('test.superadminId'), $changedOrderDetails[0]->id_customer); @@ -170,7 +173,15 @@ public function testEditOrderDetailCustomerAsSuperadminPartedIn2And5() } - private function editOrderDetailCustomer($orderDetailId, $customerId, $editCustomerReason, $amount) + public function testEditOrderDetailCustomerAsSuperadminNoEmailsSent() + { + $this->loginAsSuperadmin(); + $this->editOrderDetailCustomer($this->orderDetailIdA, $this->newCustomerId, $this->editCustomerReason, $this->editCustomerAmount, false); + $this->runAndAssertQueue(); + $this->assertNoMailSent(); + } + + private function editOrderDetailCustomer($orderDetailId, $customerId, $editCustomerReason, $amount, $sendEmailToCustomers) { $this->post( '/admin/order-details/editCustomer/', @@ -178,7 +189,8 @@ private function editOrderDetailCustomer($orderDetailId, $customerId, $editCusto 'orderDetailId' => $orderDetailId, 'customerId' => $customerId, 'editCustomerReason' => $editCustomerReason, - 'amount' => $amount + 'amount' => $amount, + 'sendEmailToCustomers' => $sendEmailToCustomers, ] ); } diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditNameTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditNameTest.php index 80810253ba..fcb190aa1b 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditNameTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditNameTest.php @@ -1,4 +1,6 @@ loginAsSuperadmin(); diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPickupDayTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPickupDayTest.php index 3da7767460..6268720f1f 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPickupDayTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPickupDayTest.php @@ -1,4 +1,6 @@ editPickupDayOfOrderDetails([$this->orderDetailIdA, $this->orderDetailIdB], '2018-09-07', $reason, true); $this->assertJsonOk(); $this->runAndAssertQueue(); - $this->assertMailContainsAt(0, 'Du kannst unseren Newsletter im Admin-Bereich unter "Meine Daten" abonnieren.'); + $this->assertMailContainsAt(0, 'Du kannst unseren Newsletter im Admin-Bereich unter "Meine Daten" abonnieren.'); } public function testEditPickupDayAsSuperadminWithoutEmailsOk() diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPriceTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPriceTest.php index 7edfc8d52a..effa20172c 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPriceTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPriceTest.php @@ -1,4 +1,6 @@ changeManufacturer(5, 'anonymize_customers', 1); $this->loginAsSuperadmin(); $this->editOrderDetailPrice($this->orderDetailIdA, $this->newPrice, $this->editPriceReason, true); @@ -79,7 +84,8 @@ public function testEditOrderDetailPriceAsSuperadminWithEnabledNotification() $this->assertOrderDetailProductPriceChangedEmails(0, $expectedToEmails); $this->assertMailSentToAt(1, Configure::read('test.loginEmailVegetableManufacturer')); - + $this->assertMailContainsHtmlAt(1, 'Hallo Demo Gemüse-Hersteller'); + $this->assertMailContainsHtmlAt(1, 'D.S. - ID 92'); } public function testEditOrderDetailPriceIfPriceWasZero() diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPurchasePriceTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPurchasePriceTest.php index 7fefa8159e..c57541ca06 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPurchasePriceTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditPurchasePriceTest.php @@ -1,4 +1,6 @@ changeConfiguration('FCS_PURCHASE_PRICE_ENABLED', 1); diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditQuantityTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditQuantityTest.php index 89566b13b0..2f7444ff8f 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditQuantityTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetails/OrderDetailsControllerEditQuantityTest.php @@ -1,4 +1,6 @@ loginAsSuperadmin(); $cart = $this->preparePricePerUnitOrder(); $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailQuantity($orderDetailId, -1, 'reason'); + $this->editOrderDetailQuantity($orderDetailId, -1); $this->assertEquals($this->getJsonDecodedContent()->msg, 'Das gelieferte Gewicht ist nicht gültig.'); } public function testEditOrderDetailQuantityAsSuperadminDifferentQuantity() { + + $this->changeManufacturer(4, 'anonymize_customers', 1); $this->loginAsSuperadmin(); $cart = $this->preparePricePerUnitOrder(); $newQuantity = 800.584; $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailQuantity($orderDetailId, $newQuantity, false); + $this->editOrderDetailQuantity($orderDetailId, $newQuantity); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId]); @@ -50,11 +56,13 @@ public function testEditOrderDetailQuantityAsSuperadminDifferentQuantity() $this->assertMailSubjectContainsAt(1, 'Gewicht angepasst für "Forelle : Stück": 800,584 g'); $this->assertMailContainsHtmlAt(1, '800,584 g'); - $this->assertMailContainsHtmlAt(1, 'Demo Superadmin'); + $this->assertMailContainsHtmlAt(1, 'Hallo Demo Superadmin'); $this->assertMailContainsHtmlAt(1, 'Der Grundpreis beträgt 1,50 € / 100 g'); $this->assertMailSentToAt(1, Configure::read('test.loginEmailSuperadmin')); - $this->assertMailSentWithAt(1, Configure::read('test.loginEmailMeatManufacturer'), 'cc'); + $this->assertMailSentToAt(2, Configure::read('test.loginEmailMeatManufacturer')); + $this->assertMailContainsHtmlAt(2, 'Hallo Demo Fleisch-Hersteller'); + $this->assertMailContainsHtmlAt(2, 'D.S. - ID 92'); } public function testEditOrderDetailQuantityAsSuperadminDifferentQuantityPurchasePriceAvailable() @@ -66,7 +74,7 @@ public function testEditOrderDetailQuantityAsSuperadminDifferentQuantityPurchase $newQuantity = 800.584; $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailQuantity($orderDetailId, $newQuantity, false); + $this->editOrderDetailQuantity($orderDetailId, $newQuantity); $changedOrderDetails = $this->OrderDetail->find('all', [ 'conditions' => [ @@ -92,7 +100,7 @@ public function testEditOrderDetailQuantityAsSuperadminSameQuantity() $newQuantity = 700; $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailQuantity($orderDetailId, $newQuantity, false); + $this->editOrderDetailQuantity($orderDetailId, $newQuantity); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId]); @@ -107,36 +115,13 @@ public function testEditOrderDetailQuantityAsSuperadminSameQuantity() $this->assertMailCount(1); } - public function testEditOrderDetailQuantityAsSuperadminDoNotChangePrice() - { - $this->loginAsSuperadmin(); - - $cart = $this->preparePricePerUnitOrder(); - - $newQuantity = 800.854; - $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - - $this->editOrderDetailQuantity($orderDetailId, $newQuantity, true); - - $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId]); - - $this->assertEquals($changedOrderDetails[0]->total_price_tax_incl, $changedOrderDetails[0]->total_price_tax_incl); - $this->assertEquals($changedOrderDetails[0]->total_price_tax_excl, $changedOrderDetails[0]->total_price_tax_excl); - $this->assertEquals($newQuantity, $changedOrderDetails[0]->order_detail_unit->product_quantity_in_units); - - $this->assertEquals($changedOrderDetails[0]->tax_unit_amount, $changedOrderDetails[0]->tax_unit_amount); - $this->assertEquals($changedOrderDetails[0]->tax_total_amount, $changedOrderDetails[0]->tax_total_amount); - - $this->assertMailCount(1); - } - public function testEditOrderDetailQuantityAsSuperadminEmailDisabledWithConfig() { Configure::write('app.sendEmailWhenOrderDetailQuantityChanged', false); $this->loginAsSuperadmin(); $cart = $this->preparePricePerUnitOrder(); $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailQuantity($orderDetailId, 800.854, false); + $this->editOrderDetailQuantity($orderDetailId, 800.854); $this->assertMailCount(1); } @@ -145,9 +130,9 @@ public function testEditOrderDetailQuantityAsSuperadminUserUsedWrongUnit() $this->loginAsSuperadmin(); $cart = $this->preparePricePerUnitOrder(); $orderDetailId = $cart->cart_products[0]->order_detail->id_order_detail; - $this->editOrderDetailQuantity($orderDetailId, 0.7, false); + $this->editOrderDetailQuantity($orderDetailId, 0.7); $this->assertEquals($this->getJsonDecodedContent()->msg, 'Der neue Preis wäre 0,01 € für 0,7 g. Bitte überprüfe die Einheit.'); - $this->editOrderDetailQuantity($orderDetailId, 800000, false); + $this->editOrderDetailQuantity($orderDetailId, 800000); $this->assertEquals($this->getJsonDecodedContent()->msg, 'Der neue Preis wäre 12.000,00 € für 800.000 g. Bitte überprüfe die Einheit.'); } @@ -155,9 +140,9 @@ public function testEditOrderDetailQuantityAsSuperadminUserUsedWrongUnit() * https://github.com/foodcoopshop/foodcoopshop/issues/836 * fix is not yet implemented */ + /* public function testEditOrderDetailQuantityAsSuperadminWithHugeQuantity() { - $this->markTestSkipped(); $this->loginAsSuperadmin(); $this->OrderDetail->deleteAll([]); $this->changeConfiguration('FCS_MINIMAL_CREDIT_BALANCE', -1000); @@ -167,7 +152,7 @@ public function testEditOrderDetailQuantityAsSuperadminWithHugeQuantity() $this->finishCart(1, 1, '', null); $orderDetailId = 4; $newQuantity = 8000; - $this->editOrderDetailQuantity($orderDetailId, $newQuantity, false); + $this->editOrderDetailQuantity($orderDetailId, $newQuantity); $changedOrderDetails = $this->getOrderDetailsFromDatabase([$orderDetailId]); @@ -180,6 +165,7 @@ public function testEditOrderDetailQuantityAsSuperadminWithHugeQuantity() $this->assertEquals(18.32, $changedOrderDetails[0]->tax_total_amount); } + */ private function preparePricePerUnitOrder() { @@ -191,18 +177,16 @@ private function preparePricePerUnitOrder() return $cart; } - private function editOrderDetailQuantity($orderDetailId, $productQuantity, $doNotChangePrice) + private function editOrderDetailQuantity($orderDetailId, $productQuantity) { $this->ajaxPost( '/admin/order-details/editProductQuantity/', [ 'orderDetailId' => $orderDetailId, 'productQuantity' => $productQuantity, - 'doNotChangePrice' => $doNotChangePrice ] ); $this->runAndAssertQueue(); } - } \ No newline at end of file diff --git a/plugins/Admin/tests/TestCase/src/Controller/OrderDetailsControllerTest.php b/plugins/Admin/tests/TestCase/src/Controller/OrderDetailsControllerTest.php index fbe32dd614..c45adffe4b 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/OrderDetailsControllerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/OrderDetailsControllerTest.php @@ -1,4 +1,6 @@ assertResponseCode(403); } - public function testAddPaymentParameterPriceOk() + public function testAddPaymentParameterAmountOk() { $this->loginAsCustomer(); $jsonDecodedContent = $this->addPayment(Configure::read('test.customerId'), '65,03', 'product'); $this->assertEquals(65.03, $jsonDecodedContent->amount); } - public function testAddPaymentParameterPriceWithWhitespaceOk() + public function testAddPaymentParameterAmountWithWhitespaceOk() { $this->loginAsCustomer(); $jsonDecodedContent = $this->addPayment(Configure::read('test.customerId'), ' 24,88 ', 'product'); $this->assertEquals(24.88, $jsonDecodedContent->amount); } - public function testAddPaymentParameterPriceNegative() + public function testAddPaymentParameterAmountNegative() { $this->loginAsCustomer(); $jsonDecodedContent = $this->addPayment(Configure::read('test.customerId'), '-10', 'product'); @@ -64,7 +67,7 @@ public function testAddPaymentParameterPriceNegative() $this->assertRegExpWithUnquotedString('Der Betrag muss größer als 0 sein', $jsonDecodedContent->msg); } - public function testAddPaymentParameterPriceAlmostZero() + public function testAddPaymentParameterAmountAlmostZero() { $this->loginAsCustomer(); $jsonDecodedContent = $this->addPayment(Configure::read('test.customerId'), '0,003', 'product'); @@ -72,7 +75,7 @@ public function testAddPaymentParameterPriceAlmostZero() $this->assertRegExpWithUnquotedString('Der Betrag muss größer als 0 sein', $jsonDecodedContent->msg); } - public function testAddPaymentParameterPriceZero() + public function testAddPaymentParameterAmountZero() { $this->loginAsCustomer(); $jsonDecodedContent = $this->addPayment(Configure::read('test.customerId'), '0', 'product'); @@ -80,7 +83,7 @@ public function testAddPaymentParameterPriceZero() $this->assertRegExpWithUnquotedString('Der Betrag muss größer als 0 sein', $jsonDecodedContent->msg); } - public function testAddPaymentParameterPriceWrongNumber() + public function testAddPaymentParameterAmountWrongNumber() { $this->loginAsCustomer(); $jsonDecodedContent = $this->addPayment(Configure::read('test.customerId'), '10,--', 'product'); @@ -413,7 +416,9 @@ private function addPaymentAndAssertIncreasedCreditBalance($customerId, $amountT $jsonDecodedContent = $this->addPayment($customerId, $amountToAdd, $paymentType); $creditBalanceAfterAdd = $this->Customer->getCreditBalance($customerId); $amountToAddAsDecimal = Configure::read('app.numberHelper')->getStringAsFloat($amountToAdd); - $this->assertEquals($amountToAddAsDecimal, $creditBalanceAfterAdd - $creditBalanceBeforeAdd, 'add payment '.$paymentType.' did not increase credit balance'); + + $result = number_format($creditBalanceAfterAdd - $creditBalanceBeforeAdd, 1); + $this->assertEquals($amountToAddAsDecimal, $result, 'add payment '.$paymentType.' did not increase credit balance'); $this->assertEquals(1, $jsonDecodedContent->status); $this->assertEquals($amountToAdd, Configure::read('app.numberHelper')->formatAsDecimal($jsonDecodedContent->amount, 1)); } diff --git a/plugins/Admin/tests/TestCase/src/Controller/ProductsControllerTest.php b/plugins/Admin/tests/TestCase/src/Controller/ProductsControllerTest.php index 3d02d4a3ee..3ec5d679ce 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/ProductsControllerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/ProductsControllerTest.php @@ -1,4 +1,6 @@ get('/admin/products/changeStatus/' . $productId . '/' . $status); $product = $this->Product->find('all', [ 'conditions' => [ - 'Products.id_product' => $productId + 'Products.id_product' => $productId, ] ])->first(); - $this->assertEquals($product->active, $status, 'changing product status did not work'); + $this->assertEquals($product->active, $status); + } + + public function testChangeProductStatusBulkAsSuperadmin() + { + $this->loginAsSuperadmin(); + $productIds = [60, 102, 103]; + $status = APP_OFF; + $this->ajaxPost('/admin/products/changeStatusBulk', [ + 'productIds' => $productIds, + 'status' => APP_OFF, + ]); + $products = $this->Product->find('all', [ + 'conditions' => [ + 'Products.id_product IN' => $productIds, + ] + ]); + foreach ($products as $product) { + $this->assertEquals($product->active, $status); + } + } + + public function testChangeProductStatusBulkAsManufacturerPermisionsNotOk() + { + $this->loginAsMeatManufacturer(); + $productIds = [60, 102, 103]; + $status = APP_OFF; + $this->ajaxPost('/admin/products/changeStatusBulk', [ + 'productIds' => $productIds, + 'status' => $status, + ]); + $this->assertAccessDeniedFlashMessage(); + } + + public function testChangeProductStatusBulkAsManufacturersPermissionsOk() + { + $this->loginAsMilkManufacturer(); + $productIds = [60]; + $status = APP_OFF; + $this->ajaxPost('/admin/products/changeStatusBulk', [ + 'productIds' => $productIds, + 'status' => $status, + ]); + $products = $this->Product->find('all', [ + 'conditions' => [ + 'Products.id_product IN' => $productIds, + ] + ]); + foreach ($products as $product) { + $this->assertEquals($product->active, $status); + } } public function testEditSellingPriceWithInvalidPriceAsSuperadmin() @@ -408,7 +460,7 @@ public function testEditDeliveryRhythmOkIndividual() public function testEditDeliveryRhythmIndividualInvalidSendOrderListDay() { $this->loginAsSuperadmin(); - $response = $this->changeProductDeliveryRhythm(346, '0-individual', '2018-08-31', '2018-08-28', 2, '2019-01-01'); + $response = $this->changeProductDeliveryRhythm(346, '0-individual', '2018-08-31', '2018-08-28', '2', '2019-01-01'); $this->assertRegExpWithUnquotedString('Das Datum für den Bestellisten-Versand muss zwischen Bestellbar-bis-Datum und dem Liefertag liegen.', $response->msg); $this->assertJsonError(); } @@ -432,7 +484,7 @@ public function testEditDeliveryRhythmOkWithDatabaseAsserts() public function testEditDeliveryRhythmWeeklyInvalidSendOrderListsWeekday() { $this->loginAsSuperadmin(); - $response = $this->changeProductDeliveryRhythm(346, '1-week', '', '', 15); + $response = $this->changeProductDeliveryRhythm(346, '1-week', '', '', '15'); $this->assertRegExpWithUnquotedString('Bitte gib eine Zahl zwischen 0 und 6 an.', $response->msg); $this->assertJsonError(); } diff --git a/plugins/Admin/tests/TestCase/src/Model/Table/CartProductsTableTest.php b/plugins/Admin/tests/TestCase/src/Model/Table/CartProductsTableTest.php index dd0d23fd83..673ce02e50 100644 --- a/plugins/Admin/tests/TestCase/src/Model/Table/CartProductsTableTest.php +++ b/plugins/Admin/tests/TestCase/src/Model/Table/CartProductsTableTest.php @@ -1,4 +1,5 @@ [], 'cashless' => [ '10' => [ - 'sum_price_excl' => 35.28, - 'sum_tax' => 3.53, - 'sum_price_incl' => 38.81, + 'sum_price_excl' => 34.95, + 'sum_tax' => 3.5, + 'sum_price_incl' => 38.45, ], ], 'total' => [ '10' => [ - 'sum_price_excl' => 35.28, - 'sum_tax' => 3.53, - 'sum_price_incl' => 38.81, + 'sum_price_excl' => 34.95, + 'sum_tax' => 3.5, + 'sum_price_incl' => 38.45, ], ] ], 'taxRatesSums' => [ 'cashless' => [ - 'sum_price_excl' => 35.28, - 'sum_tax' => 3.53, - 'sum_price_incl' => 38.81, + 'sum_price_excl' => 34.95, + 'sum_tax' => 3.5, + 'sum_price_incl' => 38.45, ], 'total' => [ - 'sum_price_excl' => 35.28, - 'sum_tax' => 3.53, - 'sum_price_incl' => 38.81, + 'sum_price_excl' => 34.95, + 'sum_tax' => 3.5, + 'sum_price_incl' => 38.45, ], ], ]; diff --git a/plugins/Admin/tests/TestCase/src/Model/Table/OrderDetailsTableTest.php b/plugins/Admin/tests/TestCase/src/Model/Table/OrderDetailsTableTest.php index d7abef8c3b..d4c9c2a743 100644 --- a/plugins/Admin/tests/TestCase/src/Model/Table/OrderDetailsTableTest.php +++ b/plugins/Admin/tests/TestCase/src/Model/Table/OrderDetailsTableTest.php @@ -1,4 +1,5 @@ OrderDetail->getDepositTax($gross, $amount); + $result = $this->OrderDetail->getDepositTax($gross, $amount, 20); + $result = number_format($result, 2); + $expected = number_format($expected, 2); $this->assertEquals($result, $expected); } private function assertGetDepositNet($gross, $amount, $expected) { - $result = $this->OrderDetail->getDepositNet($gross, $amount); + $result = $this->OrderDetail->getDepositNet($gross, $amount, 20); + $result = number_format($result, 2); + $expected = number_format($expected, 2); $this->assertEquals($result, $expected); } diff --git a/plugins/Admin/tests/TestCase/src/Model/Table/ProductAttributesTableTest.php b/plugins/Admin/tests/TestCase/src/Model/Table/ProductAttributesTableTest.php index 6d1af5bcdf..de08876357 100644 --- a/plugins/Admin/tests/TestCase/src/Model/Table/ProductAttributesTableTest.php +++ b/plugins/Admin/tests/TestCase/src/Model/Table/ProductAttributesTableTest.php @@ -1,4 +1,5 @@ - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ -class ProductsTableDeliveryRhythmTest extends AppCakeTestCase -{ - - use AppIntegrationTestTrait; - use LoginTrait; - - public $Product; - - public function setUp(): void - { - parent::setUp(); - $this->Product = $this->getTableLocator()->get('Products'); - } - - public function test1WeekWithFirstDeliveryDayAllowOrdersConfigOff() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-11-02'), - 'is_stock_product' => '0' - ] - ), - 'currentDay' => '2018-10-07', - 'result' => '2018-11-02' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1WeekWithFirstDeliveryDayAllowOrdersConfigOn() - { - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-11-02'), - 'is_stock_product' => '0' - ] - ), - 'currentDay' => '2018-10-07', - 'result' => 'delivery-rhythm-triggered-delivery-break', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1WeekNormalNoFirstDeliveryDay() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0' - ] - ), - 'currentDay' => '2018-10-07', - 'result' => '2018-10-12' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1WeekWithSendOrderListDayOneDayBeforeDefault() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 2 - ] - ), - 'currentDay' => '2017-08-08', - 'result' => '2017-08-18' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1WeekWithSendOrderListDayTwoDaysBeforeDefault() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 1 - ] - ), - 'currentDay' => '2020-04-05', - 'result' => '2020-04-10' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekWithSendOrderListDayTwoDaysBeforeDefaultAndChangedSendOrderListsDayDeltaAllowOrdersConfigOff() - { - $this->changeConfiguration('FCS_SEND_FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 3); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2021-02-05'), - 'delivery_rhythm_send_order_list_weekday' => 0, - ] - ), - 'currentDay' => '2021-08-01', - 'result' => '2021-08-20', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekWithSendOrderListDayTwoDaysBeforeDefaultAndChangedSendOrderListsDayDeltaAllowOrdersConfigOn() - { - $this->changeConfiguration('FCS_SEND_FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 3); - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2021-02-05'), - 'delivery_rhythm_send_order_list_weekday' => 0, - ] - ), - 'currentDay' => '2021-08-01', - 'result' => 'delivery-rhythm-triggered-delivery-break', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1WeekWithSendOrderListDayMondayAllowOrdersConfigOff() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 1, - ] - ), - 'currentDay' => '2022-02-01', - 'result' => '2022-02-11', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1WeekWithSendOrderListDayMondayAllowOrdersConfigOn() - { - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 1, - ] - ), - 'currentDay' => '2022-02-01', - 'result' => 'delivery-rhythm-triggered-delivery-break', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekWithSendOrderListDayMondayAllowOrdersConfigOff() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 1, - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-01') - ] - ), - 'currentDay' => '2019-02-25', - 'result' => '2019-03-15', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekWithSendOrderListDayMondayAllowOrdersConfigOn() - { - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 1, - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-01') - ] - ), - 'currentDay' => '2019-02-25', - 'result' => 'delivery-rhythm-triggered-delivery-break', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekWithSendOrderListDayThursday() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 4, - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-01') - ] - ), - 'currentDay' => '2019-03-08', - 'result' => '2019-03-15' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1MonthFirstFridayWithSendOrderListDaySunday() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0', - 'delivery_rhythm_send_order_list_weekday' => 1, - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2020-10-02') - ] - ), - 'currentDay' => '2020-09-28', - 'result' => '2020-11-06' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekNotCurrentWeekAAllowOrderConfigOff() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-10') - ] - ), - 'currentDay' => '2018-08-14', - 'result' => '2018-08-24', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekNotCurrentWeekAAllowOrderConfigOn() - { - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-10') - ] - ), - 'currentDay' => '2018-08-14', - 'result' => 'delivery-rhythm-triggered-delivery-break', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekNotCurrentWeekBAllowOrderConfigOff() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-07-06') - ] - ), - 'currentDay' => '2018-09-15', - 'result' => '2018-09-28', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekNotCurrentWeekBAllowOrderConfigOn() - { - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-07-06') - ] - ), - 'currentDay' => '2018-09-15', - 'result' => 'delivery-rhythm-triggered-delivery-break', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekCurrentWeekAllowOrderConfigOff() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03') - ] - ), - 'currentDay' => '2018-08-14', - 'result' => '2018-08-17', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekCurrentWeekAllowOrderConfigOn() - { - $this->changeConfiguration('FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY', 1); - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03') - ] - ), - 'currentDay' => '2018-08-14', - 'result' => '2018-08-17', - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2WeekD() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2019-03-22') - ] - ), - 'currentDay' => '2019-03-15', - 'result' => '2019-03-22' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test4Week() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'week', - 'delivery_rhythm_count' => '4', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03') - ] - ), - 'currentDay' => '2018-08-07', - 'result' => '2018-08-31' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test1MonthFirstWeekdayOfMonthA() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2017-08-07', - 'result' => '2017-09-01' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function testFirstWeekdayOfMonthB() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '1', - 'is_stock_product' => '1', - ] - ), - 'currentDay' => '2017-08-07', - 'result' => '2017-08-11' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function testLastMonthLastWeekdayOfMonthA() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '0', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2018-09-13', - 'result' => '2018-09-28' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function testLastMonthLastWeekdayOfMonthB() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '0', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2018-08-07', - 'result' => '2018-08-31' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function testLastMonthLastWeekdayOfMonthC() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2021-02-26'), - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '0', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2021-01-20', - 'result' => '2021-02-26' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test2Month() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '2', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2020-11-20', - 'result' => '2020-12-11' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test3Month() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '3', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2020-11-20', - 'result' => '2020-12-18' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function test4Month() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'month', - 'delivery_rhythm_count' => '4', - 'is_stock_product' => '0', - ] - ), - 'currentDay' => '2020-11-30', - 'result' => '2020-12-25' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - public function testIndividual() - { - $data = [ - 'product' => $this->Product->newEntity( - [ - 'delivery_rhythm_type' => 'individual', - 'delivery_rhythm_count' => '0', - 'is_stock_product' => '0', - 'delivery_rhythm_first_delivery_day' => new FrozenDate('2018-08-03') - ] - ), - 'currentDay' => '2017-08-07', - 'result' => '2018-08-03' - ]; - $this->assertPickupDay($data['product'], $data['currentDay'], $data['result']); - } - - private function assertPickupDay($product, $currentDay, $expectedResult) - { - $result = $this->Product->calculatePickupDayRespectingDeliveryRhythm($product, $currentDay); - $this->assertEquals($expectedResult, $result); - } - -} diff --git a/plugins/Admin/tests/TestCase/src/Model/Table/ProductsTableTest.php b/plugins/Admin/tests/TestCase/src/Model/Table/ProductsTableTest.php index 0fbf6f7a4c..02f88de547 100644 --- a/plugins/Admin/tests/TestCase/src/Model/Table/ProductsTableTest.php +++ b/plugins/Admin/tests/TestCase/src/Model/Table/ProductsTableTest.php @@ -1,4 +1,5 @@ WWW_ROOT . '/img/tests/test-image.jpg'] + [$productId => WWW_ROOT . 'img/tests/test-image.jpg'] ]; $this->Product->changeImage($products); @@ -89,7 +90,7 @@ public function testChangeImageInvalidImage() { $productId = 346; $products = [ - [$productId => Configure::read('app.cakeServerName') . '/css/global.css'] + [$productId => Configure::read('App.fullBaseUrl') . '/css/global.css'] ]; $exceptionThrown = false; @@ -106,7 +107,7 @@ public function testChangeImageNonExistingFile() { $productId = 346; $products = [ - [$productId => Configure::read('app.cakeServerName') . '/img/tests/non-existing-file.jpg'] + [$productId => Configure::read('App.fullBaseUrl') . '/img/tests/non-existing-file.jpg'] ]; $exceptionThrown = false; diff --git a/plugins/Admin/tests/TestCase/src/Model/Table/PurchasePriceProductsTableTest.php b/plugins/Admin/tests/TestCase/src/Model/Table/PurchasePriceProductsTableTest.php index 82a0132931..19adc3ec93 100644 --- a/plugins/Admin/tests/TestCase/src/Model/Table/PurchasePriceProductsTableTest.php +++ b/plugins/Admin/tests/TestCase/src/Model/Table/PurchasePriceProductsTableTest.php @@ -1,4 +1,5 @@ .has-separator:after { content: ' / '; } .cke_dialog_container { - z-index: 2147483650 ! important; /* open upload from dialog - 2 more than .ui-dialog */ + z-index: 2147483650 ! important; /* open upload from dialog */ } #manufacturer-options-edit-form h3 { margin-top: 15px; @@ -600,9 +595,6 @@ body.payments .bottom-button-container { width: 20px; height: 20px; } -.ui-dialog.has-datepicker { - z-index: 2147483646 ! important; -} #product-is-stock-product-edit-form input { float: right ! important; } @@ -730,7 +722,8 @@ form.fcs-form .input.text .after.small, form.fcs-form .input.number .after.small, form.fcs-form .input.tel .after.small, form.fcs-form .input.select .after.small, -form.fcs-form .input.fcs-upload .after.small { +form.fcs-form .input.fcs-upload .after.small, +form.fcs-form .input.time .after.small { float: right; line-height: 28px; margin-left: 5px; @@ -741,9 +734,7 @@ div.date-field-wrapper { } ul.nav-tabs { background: #efefef; - margin-top: -2px; - margin-left: -2px; - width: 1070px; + width: 1072px; padding-bottom: 1px; } ul.nav-tabs > li > a { @@ -754,6 +745,7 @@ ul.nav-tabs > li > a { ul.nav-tabs a:hover, ul.nav-tabs li.active a { background-color: #fff; + text-decoration: underline ! important; } a.btn-arrow { display: inline-block; @@ -851,7 +843,8 @@ body.reports.payment .payment-approval-comment { margin-right: 2px; } body.reports.payment form#csv-upload { - padding: 10px 0 5px 0; + padding: 10px 0 5px 10px; + border-bottom: 1px solid #efefef; } body.reports.payment form#csv-upload label { margin-right: 10px; @@ -969,4 +962,8 @@ body.feedbacks textarea { } body.feedbacks form div.error-message { margin-top: -10px; +} +body.manufacturers .generate-order-lists-tooltip a.btn { + text-align: left; + text-decoration: none ! important; } \ No newline at end of file diff --git a/plugins/Admin/webroot/css/mobile.css b/plugins/Admin/webroot/css/mobile.css index f0ce1bc7c7..5dcd7cb764 100644 --- a/plugins/Admin/webroot/css/mobile.css +++ b/plugins/Admin/webroot/css/mobile.css @@ -33,7 +33,7 @@ left: 0; width: 100vw; max-width: none; - padding: 3px; + border-left: none; } .filter-container .right { margin-top: 1px; @@ -57,13 +57,9 @@ form.fcs-form .bootstrap-select, /* sic! necessary for bootstrap selects within clear: both; margin-left: 0px; } -#filter-loader { - left: 50%; - margin-left: -75px; -} .sb-toggle-left { float: left; - margin: 3px 5px 0 3px; + margin: 0 5px 0 0; border-radius: 3px; padding: 2px 5px 0px 5px; background: #fff; @@ -81,6 +77,7 @@ form.fcs-form .bootstrap-select, /* sic! necessary for bootstrap selects within #container { float: left; min-width: 100%; + overflow: initial ! important; /* for sticky table headers */ } body.pages.home #container, body.pages.home #content, @@ -96,11 +93,6 @@ body.pages.home .filter-container, body.pages.home #content { background: transparent; } -.ui-dialog { - min-width: 200px ! important; - width: 98% ! important; - max-width: 450px ! important; -} .product-attributes-dropdown { width: 100%; } @@ -140,10 +132,6 @@ div.cke { form.fcs-form .input .after.small { width: 100%; } -.ui-dialog-content #flashMessage { - width: 96%; - left: 2%; -} table.list th, table.list tr.fake-th td { word-break: normal; diff --git a/plugins/Admin/webroot/js/admin.js b/plugins/Admin/webroot/js/admin.js index 1ae9d36307..bd8cf13580 100644 --- a/plugins/Admin/webroot/js/admin.js +++ b/plugins/Admin/webroot/js/admin.js @@ -16,12 +16,14 @@ foodcoopshop.Admin = { init: function () { this.initFilter(); this.improveTableLayout(); - foodcoopshop.Helper.initJqueryUiIcons(); + foodcoopshop.ColorMode.init(); foodcoopshop.Helper.showContent(); foodcoopshop.Helper.initMenu(); foodcoopshop.ModalLogout.init(); + this.initRowMarkerAll(); this.setMenuFixed(); this.adaptContentMargin(); + this.initStickyTableHeader(); foodcoopshop.Helper.initScrolltopButton(); }, @@ -44,7 +46,7 @@ foodcoopshop.Admin = { } - $('.row-marker').on('click', function() { + $('.row-marker,#row-marker-all').on('click', function () { var selectedOrderDetailIds = foodcoopshop.Admin.getSelectedOrderDetailIds(); @@ -86,7 +88,7 @@ foodcoopshop.Admin = { addLoaderToSyncProductDataButton : function (button) { button.on('click', function () { - foodcoopshop.Helper.addSpinnerToButton($(this), 'fa-arrow-circle-left'); + foodcoopshop.Helper.addSpinnerToButton($(this), 'fa-arrow-circle-right'); foodcoopshop.Helper.disableButton($(this)); }); }, @@ -95,15 +97,17 @@ foodcoopshop.Admin = { foodcoopshop.Helper.selectMainMenu('#menu', mainMenuTitle, subMenuTitle); }, - /** - * @return rowMarker dom element - */ initRowMarkerAll : function () { - var rowMarkerAll = $('input#row-marker-all').on('change', function () { + var rowMarkerAll = $('input#row-marker-all').on('click', function () { + var row; if (this.checked) { - $('input.row-marker[type="checkbox"]:not(:checked)').trigger('click'); + row = $('input.row-marker[type="checkbox"]:not(:checked)'); + row.prop('checked', true); + row.closest('tr').addClass('selected'); } else { - $('input.row-marker[type="checkbox"]:checked').trigger('click'); + row = $('input.row-marker[type="checkbox"]:checked'); + row.prop('checked', false); + row.closest('tr').removeClass('selected'); } }); return rowMarkerAll; @@ -145,7 +149,7 @@ foodcoopshop.Admin = { return ids; }, - updateObjectSelectionActionButton : function (button) { + updateObjectSelectionActionButton: function (button) { foodcoopshop.Helper.disableButton(button); if ($('table.list').find('input.row-marker[type="checkbox"]:checked').length > 0) { foodcoopshop.Helper.enableButton(button); @@ -230,9 +234,10 @@ foodcoopshop.Admin = { }, initHighlightedRowId: function (rowId) { + var newTop = $('.filter-container').height() + $(rowId).closest('table').find('tr.sort').height() + 10; $.scrollTo(rowId, 1000, { offset: { - top: -100 + top: newTop * -1, } }); $(rowId).css('background-color', 'orange'); @@ -295,7 +300,7 @@ foodcoopshop.Admin = { var button = $(btnSelector); foodcoopshop.Helper.disableButton(button); - $('table.list').find('input.row-marker[type="checkbox"]').on('click', function () { + $('table.list').find('input.row-marker[type="checkbox"],#row-marker-all').on('click', function () { foodcoopshop.Admin.updateObjectSelectionActionButton(button); }); @@ -394,11 +399,7 @@ foodcoopshop.Admin = { }, triggerFilter : function () { - $('#filter-loader').remove(); - $('#content').css('opacity', '.3'); - $('#container').prepend('
'); - var marginTop = $('.filter-container').outerHeight(); - $('#filter-loader').css('top', marginTop + 20); + foodcoopshop.Helper.showLoader(); foodcoopshop.Admin.submitFilterForm(); }, @@ -434,11 +435,6 @@ foodcoopshop.Admin = { return url; }, - addParameterToURL : function(url, param) { - url += (url.split('?')[1] ? '&':'?') + param; - return url; - }, - setMenuFixed: function () { $(window).scroll(function () { $('#menu').css('left', -$(window).scrollLeft()); @@ -453,11 +449,16 @@ foodcoopshop.Admin = { $('#menu').css('min-height', marginTop + $('#content').height() + 4); }, + initStickyTableHeader : function() { + var newTop = $('.filter-container').height(); + $('table.list th').css('top', newTop + 11); + }, + initGenerateMemberCardsOfSelectedCustomersButton : function() { var button = $('#generateMemberCardsOfSelectedCustomersButton'); foodcoopshop.Helper.disableButton(button); - $('table.list').find('input.row-marker[type="checkbox"]').on('click', function () { + $('table.list').find('input.row-marker[type="checkbox"],#row-marker-all').on('click', function () { foodcoopshop.Admin.updateObjectSelectionActionButton(button); }); @@ -471,7 +472,8 @@ foodcoopshop.Admin = { var button = $('#generateProductCardsOfSelectedProductsButton'); foodcoopshop.Helper.disableButton(button); - $('table.list').find('input.row-marker[type="checkbox"]').on('click', function () { + $('table.list').find('input.row-marker[type="checkbox"],#row-marker-all').on('click', function () { + console.log('click'); foodcoopshop.Admin.updateObjectSelectionActionButton(button); }); diff --git a/plugins/Admin/webroot/js/app.chart.js b/plugins/Admin/webroot/js/app.chart.js index bf5386307c..9ab59e5458 100644 --- a/plugins/Admin/webroot/js/app.chart.js +++ b/plugins/Admin/webroot/js/app.chart.js @@ -15,6 +15,14 @@ foodcoopshop.AppChart = { color: '#cccccc', // default color + getFontColor: function() { + return foodcoopshop.ColorMode.getColorMode() == 'dark' ? '#CDCDCD' : '#333333'; + }, + + getGridColor: function() { + return foodcoopshop.ColorMode.getColorMode() == 'dark' ? '#696969' : '#dfdfdf'; + }, + barChartOptions : { plugins: { legend: { @@ -57,8 +65,9 @@ foodcoopshop.AppChart = { ticks: { callback: function(value, index, values) { return foodcoopshop.Helper.formatFloatAsCurrency(value); - } - } + }, + }, + grid: {} }, } }, @@ -74,7 +83,7 @@ foodcoopshop.AppChart = { tooltip: { callbacks: { label: function(ctx) { - return ctx.label + ': ' + foodcoopshop.Helper.formatFloatAsCurrency(ctx.parsed); + return foodcoopshop.Helper.formatFloatAsCurrency(ctx.parsed); } } }, @@ -94,7 +103,7 @@ foodcoopshop.AppChart = { } return ''; }, - color: '#fff', + color: '#ccc', labels: { title: { font: { @@ -123,7 +132,8 @@ foodcoopshop.AppChart = { x: { grid: { display: false - } + }, + ticks: {} }, y: { beginAtZero: true, @@ -131,7 +141,8 @@ foodcoopshop.AppChart = { callback: function(value, index, values) { return foodcoopshop.Helper.formatFloatAsCurrency(value); } - } + }, + grid: {} } } }, @@ -153,9 +164,12 @@ foodcoopshop.AppChart = { pointBackgroundColor: this.color, pointRadius: 5 }], - }; + this.lineChartOptions.scales.x.ticks.color = this.getFontColor(); + this.lineChartOptions.scales.y.ticks.color = this.getFontColor(); + this.lineChartOptions.scales.y.grid.color = this.getGridColor(); + var ctx = $('#myLineChart').get(0).getContext('2d'); new Chart(ctx, { responsive : true, @@ -209,6 +223,10 @@ foodcoopshop.AppChart = { var lineChartOptions = this.lineChartOptions; lineChartOptions.plugins.legend.display = true; + lineChartOptions.scales.x.ticks.color = this.getFontColor(); + lineChartOptions.scales.y.ticks.color = this.getFontColor(); + lineChartOptions.plugins.legend.labels = {color: this.getFontColor()}; + lineChartOptions.scales.y.grid.color = this.getGridColor(); var ctx = $('#myLineChart').get(0).getContext('2d'); new Chart(ctx, { @@ -263,6 +281,7 @@ foodcoopshop.AppChart = { type: 'line', } ); + this.barChartOptions.scales.y1 = { type: 'linear', display: true, @@ -273,11 +292,17 @@ foodcoopshop.AppChart = { } }, grid: { - display: false, + display: false, }, }; + this.barChartOptions.scales.y1.ticks.color = this.getFontColor(); } + this.barChartOptions.scales.x.ticks.color = this.getFontColor(); + this.barChartOptions.scales.y.ticks.color = this.getFontColor(); + this.barChartOptions.plugins.legend.labels = {color: this.getFontColor()}; + this.barChartOptions.scales.y.grid.color = this.getGridColor(); + var ctx = $('#myBarChart').get(0).getContext('2d'); new Chart(ctx, { responsive : true, @@ -293,7 +318,7 @@ foodcoopshop.AppChart = { var pieChartData = { datasets: [{ data: dataPieChart, - borderColor: '#fff', + borderColor: '#CDCDCD', backgroundColor: backgroundColorPieChart, hoverBackgroundColor: this.color, borderWidth: 1, @@ -302,7 +327,7 @@ foodcoopshop.AppChart = { outer: { align: 'end', anchor: 'end', - color: '#333333', + color: this.getFontColor(), font: { size: 15 }, diff --git a/plugins/Admin/webroot/js/modal/modal-invoice-for-customer-add.js b/plugins/Admin/webroot/js/modal/modal-invoice-for-customer-add.js index 93b605b129..74a8c37c99 100644 --- a/plugins/Admin/webroot/js/modal/modal-invoice-for-customer-add.js +++ b/plugins/Admin/webroot/js/modal/modal-invoice-for-customer-add.js @@ -22,13 +22,13 @@ foodcoopshop.ModalInvoiceForCustomerAdd = { getHtml : function(customerName, invoiceAmount, paymentIsCashless) { var html = '

' + foodcoopshop.LocalizedJs.admin.ReallyGenerateInvoiceFor0.replaceI18n(0, '' + customerName + '') + '

'; - html += '

' + invoiceAmount + '

'; + html += '

' + invoiceAmount + '

'; html += '
'; html += ''; html += ' ' + foodcoopshop.LocalizedJs.admin.GivenAmount; html += '
'; html += '
'; - html += '

 

'; + html += '

 

'; html += '
'; html += '
'; if (paymentIsCashless) { diff --git a/plugins/Admin/webroot/js/modal/modal-order-detail-delete.js b/plugins/Admin/webroot/js/modal/modal-order-detail-delete.js index 1c22cedd15..73897d23d0 100644 --- a/plugins/Admin/webroot/js/modal/modal-order-detail-delete.js +++ b/plugins/Admin/webroot/js/modal/modal-order-detail-delete.js @@ -18,7 +18,7 @@ foodcoopshop.ModalOrderDetailDelete = { var button = $('#deleteSelectedProductsButton'); foodcoopshop.Helper.disableButton(button); - $('table.list').find('input.row-marker[type="checkbox"]').on('click', function () { + $('table.list').find('input.row-marker[type="checkbox"],#row-marker-all').on('click', function () { foodcoopshop.Admin.updateObjectSelectionActionButton(button); }); diff --git a/plugins/Admin/webroot/js/modal/modal-order-detail-pickup-day-edit.js b/plugins/Admin/webroot/js/modal/modal-order-detail-pickup-day-edit.js index b245be083f..9032f7a46b 100644 --- a/plugins/Admin/webroot/js/modal/modal-order-detail-pickup-day-edit.js +++ b/plugins/Admin/webroot/js/modal/modal-order-detail-pickup-day-edit.js @@ -18,7 +18,7 @@ foodcoopshop.ModalOrderDetailPickupDayEdit = { var button = $('#changePickupDayOfSelectedProductsButton'); foodcoopshop.Helper.disableButton(button); - $('table.list').find('input.row-marker[type="checkbox"]').on('click', function () { + $('table.list').find('input.row-marker[type="checkbox"],#row-marker-all').on('click', function () { foodcoopshop.Admin.updateObjectSelectionActionButton(button); }); @@ -118,7 +118,7 @@ foodcoopshop.ModalOrderDetailPickupDayEdit = { new bootstrap.Modal(document.getElementById(modalSelector.replace(/#/, ''))).show(); var datepickerInput = $('#dialogChangePickupDay'); - datepickerInput.val($('.filter-container input[name="pickupDay[]"').val()); + datepickerInput.val($('.filter-container input[name="pickupDay[]"]').val()); datepickerInput.datepicker(); foodcoopshop.Helper.initCkeditor('dialogEditPickupDayReason', true); diff --git a/plugins/Admin/webroot/js/modal/modal-order-detail-product-customer-edit.js b/plugins/Admin/webroot/js/modal/modal-order-detail-product-customer-edit.js index d9811e4f0c..2e2cce568b 100644 --- a/plugins/Admin/webroot/js/modal/modal-order-detail-product-customer-edit.js +++ b/plugins/Admin/webroot/js/modal/modal-order-detail-product-customer-edit.js @@ -48,6 +48,10 @@ foodcoopshop.ModalOrderDetailProductCustomerEdit = { html += ''; html += ''; html += '
'; + html += ''; html += ''; return html; }, @@ -64,7 +68,8 @@ foodcoopshop.ModalOrderDetailProductCustomerEdit = { orderDetailId: $('#dialogOrderDetailEditCustomerOrderDetailId').val(), customerId: $('#dialogOrderDetailEditCustomerId').val(), editCustomerReason: CKEDITOR.instances['dialogEditCustomerReason'].getData().trim(), - amount: $('#dialogOrderDetailEditCustomerAmount').val() + amount: $('#dialogOrderDetailEditCustomerAmount').val(), + sendEmailToCustomers: $('#dialogEditCustomerSendEmailToCustomers:checked').length > 0 ? 1 : 0, }, { onOk: function (data) { diff --git a/plugins/Admin/webroot/js/modal/modal-order-detail-product-quantity-edit.js b/plugins/Admin/webroot/js/modal/modal-order-detail-product-quantity-edit.js index ff8c2f2a47..dbca70fd3c 100644 --- a/plugins/Admin/webroot/js/modal/modal-order-detail-product-quantity-edit.js +++ b/plugins/Admin/webroot/js/modal/modal-order-detail-product-quantity-edit.js @@ -11,6 +11,7 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ + foodcoopshop.ModalOrderDetailProductQuantityEdit = { init : function() { @@ -23,6 +24,8 @@ foodcoopshop.ModalOrderDetailProductQuantityEdit = { foodcoopshop.ModalOrderDetailProductQuantityEdit.getHtml() ); + foodcoopshop.Calculator.init(modalSelector); + foodcoopshop.Modal.bindSuccessButton(modalSelector, function() { foodcoopshop.ModalOrderDetailProductQuantityEdit.getSuccessHandler(modalSelector); }); @@ -39,27 +42,27 @@ foodcoopshop.ModalOrderDetailProductQuantityEdit = { getHtml : function() { var html = '
'; - html += '
' + foodcoopshop.LocalizedJs.admin.DeliveredWeight + ': '; + html += '
' + foodcoopshop.LocalizedJs.admin.DeliveredWeight + ': '; html += ''; html += '
'; html += ''; - html += '
'; $barcodeObject = new TCPDFBarcode($product->system_bar_code, 'C39'); //https://stackoverflow.com/a/54520065/2100184 @@ -50,7 +53,7 @@ $pdf->table .= ''; + $pdf->table .= ''; if ($product->image) { $srcProductImage = $this->Html->getProductImageSrc($product->image->id_image, 'thickbox'); diff --git a/plugins/Admin/tests/TestCase/src/Controller/ConfigurationsControllerTest.php b/plugins/Admin/tests/TestCase/src/Controller/ConfigurationsControllerTest.php index 26eae2c842..706a49fd98 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/ConfigurationsControllerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/ConfigurationsControllerTest.php @@ -1,4 +1,6 @@ {$priceAssertFunction}($priceRegExp, $this->_response, 'price expected: ' . $expectPrice); + $this->{$priceAssertFunction}($priceRegExp, $this->_response->getBody()->__toString(), 'price expected: ' . $expectPrice); } } } diff --git a/plugins/Admin/tests/TestCase/src/Controller/FeedbacksControllerTest.php b/plugins/Admin/tests/TestCase/src/Controller/FeedbacksControllerTest.php index cea42c485b..aedf961a33 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/FeedbacksControllerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/FeedbacksControllerTest.php @@ -1,4 +1,6 @@ toArray(); $paymentIds = Hash::extract($payments, '{n}.id'); - $response = $this->ajaxPost( + $this->ajaxPost( '/admin/invoices/cancel/', [ 'invoiceId' => $invoice->id, ] ); - $response = json_decode($this->_response); + $response = json_decode($this->_response->getBody()->__toString()); $this->runAndAssertQueue(); $invoices = $this->Invoice->find('all', [ diff --git a/plugins/Admin/tests/TestCase/src/Controller/ListsControllerTest.php b/plugins/Admin/tests/TestCase/src/Controller/ListsControllerTest.php index d257d22c01..e5bc25871a 100644 --- a/plugins/Admin/tests/TestCase/src/Controller/ListsControllerTest.php +++ b/plugins/Admin/tests/TestCase/src/Controller/ListsControllerTest.php @@ -1,11 +1,11 @@ changeManufacturer(4, 'anonymize_customers', 1); $this->exec('send_order_lists 2018-01-31'); $this->runAndAssertQueue(); $listPageUrl = $this->Slug->getOrderLists().'?dateFrom=02.02.2018'; - $folder = new Folder(Configure::read('app.folder_order_lists').DS.'2018'.DS.'02'); - $objects = $folder->read(); - $downloadFileName = $objects[1][0]; - $orderListDownloadUrl = '/admin/lists/getOrderList?file=2018/02/'.$downloadFileName; + $path = realpath(Configure::read('app.folder_order_lists').DS.'2018'.DS.'02'); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::SELF_FIRST); + + $files = []; + foreach ($objects as $name => $object) { + if (!preg_match('/\.pdf$/', $name)) { + continue; + } + $files[] = str_replace(Configure::read('app.folder_order_lists'), '', $object->getPathName()); + } + sort($files); + + $orderListDownloadUrlClearText = '/admin/lists/getOrderList?file=' . $files[0]; + $orderListDownloadUrlAnonymized = '/admin/lists/getOrderList?file=' . $files[6]; // check list page as manufacturer $this->loginAsMeatManufacturer(); @@ -99,24 +112,37 @@ public function testAccessOrderListPageAndDownloadableFile() $this->assertResponseNotContains('Demo Milch-HerstellerDemo Fleisch-HerstellerDemo Gemüse-HerstellerDemo Milch-Hersteller
'; } - return $outString; } diff --git a/src/Shell/SendOrderListsShell.php b/src/Command/SendOrderListsCommand.php similarity index 88% rename from src/Shell/SendOrderListsShell.php rename to src/Command/SendOrderListsCommand.php index eab3669f51..5aa2844831 100644 --- a/src/Shell/SendOrderListsShell.php +++ b/src/Command/SendOrderListsCommand.php @@ -1,4 +1,6 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ -namespace App\Shell; +namespace App\Command; +use App\Lib\DeliveryRhythm\DeliveryRhythm; +use Cake\Console\Arguments; +use Cake\Console\ConsoleIo; use Cake\Core\Configure; use Cake\I18n\FrozenDate; use Cake\Utility\Hash; -class SendOrderListsShell extends AppShell +class SendOrderListsCommand extends AppCommand { - public function main() + public $cronjobRunDay; + public $ActionLog; + public $Manufacturer; + public $OrderDetail; + public $QueuedJobs; + public $Product; + + public function execute(Arguments $args, ConsoleIo $io) { - parent::main(); - $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); + $this->ActionLog = $this->getTableLocator()->get('ActionLogs'); $this->Manufacturer = $this->getTableLocator()->get('Manufacturers'); + $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); $this->QueuedJobs = $this->getTableLocator()->get('Queue.QueuedJobs'); - // $this->cronjobRunDay can is set in unit test - if (!isset($this->args[0])) { + if (!$args->getArgumentAt(0)) { $this->cronjobRunDay = Configure::read('app.timeHelper')->getCurrentDateForDatabase(); } else { - $this->cronjobRunDay = $this->args[0]; + $this->cronjobRunDay = $args->getArgumentAt(0); } if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { $pickupDay = $this->cronjobRunDay; } else { - $pickupDay = Configure::read('app.timeHelper')->getNextDeliveryDay(strtotime($this->cronjobRunDay)); + $pickupDay = DeliveryRhythm::getNextDeliveryDay(strtotime($this->cronjobRunDay)); } // 1) get all manufacturers (not only active ones) @@ -112,15 +123,15 @@ public function main() continue; } - $pickupDayFormated = new FrozenDate($pickupDayDbFormat); - $pickupDayFormated = $pickupDayFormated->i18nFormat( + $pickupDayFormatted = new FrozenDate($pickupDayDbFormat); + $pickupDayFormatted = $pickupDayFormatted->i18nFormat( Configure::read('app.timeHelper')->getI18Format('DateLong2') ); $orderDetailIds = Hash::extract($orderDetails, '{n}.id_order_detail'); $this->QueuedJobs->createJob('GenerateOrderList', [ 'pickupDayDbFormat' => $pickupDayDbFormat, - 'pickupDayFormated' => $pickupDayFormated, + 'pickupDayFormatted' => $pickupDayFormatted, 'orderDetailIds' => $orderDetailIds, 'manufacturerId' => $manufacturer->id_manufacturer, 'manufactuerName' => $manufacturer->name, @@ -133,9 +144,9 @@ public function main() $this->resetQuantityToDefaultQuantity($allOrderDetails); - $this->out($outString); + $io->out($outString); - return true; + return static::CODE_SUCCESS; } @@ -171,15 +182,15 @@ protected function getActionLogData($orderDetails, $manufacturers, $pickupDay): if (in_array($manufacturer->id_manufacturer, array_keys($tmpActionLogDatas))) { ksort($tmpActionLogDatas[$manufacturer->id_manufacturer]); foreach($tmpActionLogDatas[$manufacturer->id_manufacturer] as $pickupDayDbFormat => $tmpActionLogData) { - $pickupDayFormated = new FrozenDate($pickupDayDbFormat); - $pickupDayFormated = $pickupDayFormated->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateLong2')); - $identifier = $manufacturer->id_manufacturer . '-' . $pickupDayFormated; + $pickupDayFormatted = new FrozenDate($pickupDayDbFormat); + $pickupDayFormatted = $pickupDayFormatted->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateLong2')); + $identifier = $manufacturer->id_manufacturer . '-' . $pickupDayFormatted; $newData = '- '; $newData .= html_entity_decode($manufacturer->name) . ': ' . __('{0,plural,=1{1_product} other{#_products}}', [$tmpActionLogData['order_detail_amount_sum']]) . ' / ' . Configure::read('app.numberHelper')->formatAsCurrency($tmpActionLogData['order_detail_price_sum']); if ($pickupDayDbFormat != $pickupDay) { - $newData .= ' / ' . __('Delivery_day') . ': ' . $pickupDayFormated; + $newData .= ' / ' . __('Delivery_day') . ': ' . $pickupDayFormatted; } $actionLogDatas[] = $newData; } diff --git a/src/Shell/TestCronjobWithSocketExceptionShell.php b/src/Command/TestCronjobCommand.php similarity index 65% rename from src/Shell/TestCronjobWithSocketExceptionShell.php rename to src/Command/TestCronjobCommand.php index dcd8daa014..74a75d7bc9 100644 --- a/src/Shell/TestCronjobWithSocketExceptionShell.php +++ b/src/Command/TestCronjobCommand.php @@ -1,4 +1,6 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ -namespace App\Shell; - -use Cake\Network\Exception\SocketException; +namespace App\Command; +use Cake\Console\Arguments; +use Cake\Console\ConsoleIo; -class TestCronjobWithSocketExceptionShell extends AppShell +class TestCronjobCommand extends AppCommand { - public function main() + public function execute(Arguments $args, ConsoleIo $io) { - throw new SocketException(); + return static::CODE_SUCCESS; } } diff --git a/src/Shell/TestCronjobWithInvalidParameterExceptionShell.php b/src/Command/TestCronjobWithInvalidParameterExceptionCommand.php similarity index 70% rename from src/Shell/TestCronjobWithInvalidParameterExceptionShell.php rename to src/Command/TestCronjobWithInvalidParameterExceptionCommand.php index 71f326c3f0..96ba85de86 100644 --- a/src/Shell/TestCronjobWithInvalidParameterExceptionShell.php +++ b/src/Command/TestCronjobWithInvalidParameterExceptionCommand.php @@ -1,4 +1,6 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ -namespace App\Shell; +namespace App\Command; +use Cake\Console\Arguments; +use Cake\Console\ConsoleIo; use App\Lib\Error\Exception\InvalidParameterException; -class TestCronjobWithInvalidParameterExceptionShell extends AppShell +class TestCronjobWithInvalidParameterExceptionCommand extends AppCommand { - public function main() + public function execute(Arguments $args, ConsoleIo $io) { throw new InvalidParameterException(); } diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 5889ebc0eb..4ed9c8095c 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -1,4 +1,5 @@ response->getBody()->__toString(); + if ($this->protectEmailAddresses) { + $newOutput = OutputFilter::protectEmailAdresses($newOutput); + } + if (Configure::check('app.outputStringReplacements')) { - $newOutput = OutputFilter::replace($this->response->getBody(), Configure::read('app.outputStringReplacements')); - $this->response = $this->response->withStringBody($newOutput); + $newOutput = OutputFilter::replace($newOutput, Configure::read('app.outputStringReplacements')); } + $this->response = $this->response->withStringBody($newOutput); } /** diff --git a/src/Controller/BlogPostsController.php b/src/Controller/BlogPostsController.php index 400112233c..5df5c3031a 100644 --- a/src/Controller/BlogPostsController.php +++ b/src/Controller/BlogPostsController.php @@ -1,4 +1,5 @@ getRequest()->getEnv('ORIGINAL_REQUEST_METHOD') == 'POST') { + // no spam protected email output in input field when email address is used in comment text + $this->protectEmailAddresses = false; + } + $this->set('title_for_layout', __('Finish_cart')); if ($this->AppAuth->Cart->isCartEmpty()) { @@ -131,7 +138,7 @@ public function orderSuccessful($cartId) $this->set('cart', $cart); $this->BlogPost = $this->getTableLocator()->get('BlogPosts'); - $blogPosts = $this->BlogPost->findBlogPosts($this->AppAuth); + $blogPosts = $this->BlogPost->findBlogPosts($this->AppAuth, null, true); $this->set('blogPosts', $blogPosts); $this->set('title_for_layout', __('Your_order_has_been_placed')); @@ -251,8 +258,8 @@ private function doAddOrderToCart($deliveryDate) $formattedDeliveryDate = strtotime($deliveryDate); - $dateFrom = strtotime(Configure::read('app.timeHelper')->formatToDbFormatDate(Configure::read('app.timeHelper')->getOrderPeriodFirstDay($formattedDeliveryDate))); - $dateTo = strtotime(Configure::read('app.timeHelper')->formatToDbFormatDate(Configure::read('app.timeHelper')->getOrderPeriodLastDay($formattedDeliveryDate))); + $dateFrom = strtotime(Configure::read('app.timeHelper')->formatToDbFormatDate(DeliveryRhythm::getOrderPeriodFirstDayByDeliveryDay($formattedDeliveryDate))); + $dateTo = strtotime(Configure::read('app.timeHelper')->formatToDbFormatDate(DeliveryRhythm::getOrderPeriodLastDayByDeliveryDay($formattedDeliveryDate))); $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); $orderDetails = $this->OrderDetail->getOrderDetailQueryForPeriodAndCustomerId($dateFrom, $dateTo, $this->AppAuth->getUserId()); @@ -323,7 +330,7 @@ public function ajaxAdd() $ids = $this->Product->getProductIdAndAttributeId($initialProductId); $amount = (int) $this->getRequest()->getData('amount'); $orderedQuantityInUnits = Configure::read('app.numberHelper')->getStringAsFloat( - $this->getRequest()->getData('orderedQuantityInUnits') + (string) $this->getRequest()->getData('orderedQuantityInUnits') ); $this->CartProduct = $this->getTableLocator()->get('CartProducts'); diff --git a/src/Controller/CategoriesController.php b/src/Controller/CategoriesController.php index 38d02085e3..4a5a8ba3b0 100644 --- a/src/Controller/CategoriesController.php +++ b/src/Controller/CategoriesController.php @@ -1,4 +1,5 @@ getController()->getRequest()->getSession()->read('Auth.Manufacturer.name'); } + public function getManufacturerAnonymizeCustomers() + { + if (! $this->isManufacturer()) { + throw new \Exception('logged user is no manufacturer'); + } + return $this->getController()->getRequest()->getSession()->read('Auth.Manufacturer.anonymize_customers'); + } + public function getManufacturerVariableMemberFee() { if (! $this->isManufacturer()) { @@ -258,7 +267,7 @@ public function isSelfServiceModeByReferer() '/' . __('route_cart') . '/ajaxRemove/' ]; if (isset($serverParams['HTTP_REFERER'])) { - $result = preg_match('`' . preg_quote(Configure::read('app.cakeServerName')) . '/' . __('route_self_service') . '`', $serverParams['HTTP_REFERER']); + $result = preg_match('`' . preg_quote(Configure::read('App.fullBaseUrl')) . '/' . __('route_self_service') . '`', $serverParams['HTTP_REFERER']); } if (!in_array($serverParams['REQUEST_URI'], $requestUriAllowed)) { $result = false; diff --git a/src/Controller/Component/CartComponent.php b/src/Controller/Component/CartComponent.php index 20b0b00ebe..4bb89eff14 100644 --- a/src/Controller/Component/CartComponent.php +++ b/src/Controller/Component/CartComponent.php @@ -1,7 +1,9 @@ get('Carts'); $patchedEntity = $cc->patchEntity( $cc->get($this->getCartId()), [ - 'status' => APP_OFF - ] + 'status' => APP_OFF, + ], + ['validate' => false], ); $cc->save($patchedEntity); return $patchedEntity; @@ -146,6 +149,134 @@ public function isCartEmpty() return $isEmpty; } + protected function getProductContain(): array + { + $contain = [ + 'Manufacturers', + 'Manufacturers.AddressManufacturers', + 'StockAvailables', + 'ProductAttributes.StockAvailables', + 'ProductAttributes.ProductAttributeCombinations', + 'Taxes', + ]; + if (Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED')) { + $contain[] = 'UnitProducts'; + $contain[] = 'PurchasePriceProducts.Taxes'; + $contain[] = 'ProductAttributes.PurchasePriceProductAttributes'; + $contain[] = 'ProductAttributes.UnitProductAttributes'; + } + return $contain; + } + + protected function saveCart($cart, $orderDetails2save, $stockAvailable2saveData, $stockAvailable2saveConditions, $customerSelectedPickupDay, $products) + { + + $this->saveOrderDetails($orderDetails2save); + $this->saveStockAvailable($stockAvailable2saveData, $stockAvailable2saveConditions); + + $manufacturersThatReceivedInstantOrderNotification = $this->sendInstantOrderNotificationToManufacturers($cart['CartProducts']); + $this->sendStockAvailableLimitReachedEmailToManufacturer($cart['Cart']->id_cart); + + $pickupDayEntities = null; + if (Configure::read('appDb.FCS_ORDER_COMMENT_ENABLED')) { + $pickupDayEntities = $cart['Cart']->pickup_day_entities; + $this->Cart->PickupDayEntities->saveMany($pickupDayEntities); + } + + $cart = $this->AppAuth->getCart(); // to get attached order details + $this->AppAuth->setCart($cart); + $cart['Cart'] = $this->markAsSaved(); // modified timestamp is needed later on! + + $cartType = $this->AppAuth->getCartType(); + $userIdForActionLog = $this->AppAuth->getUserId(); + + switch($cartType) { + case $this->Cart::CART_TYPE_WEEKLY_RHYTHM; + $actionLogType = 'customer_order_finished'; + $message = __('Your_order_has_been_placed_succesfully.'); + $messageForActionLog = __('{0}_has_placed_a_new_order_({1}).', [$this->AppAuth->getUsername(), Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum())]); + $cartGroupedByPickupDay = $this->Cart->getCartGroupedByPickupDay($cart, $customerSelectedPickupDay); + $this->sendConfirmationEmailToCustomer($cart, $cartGroupedByPickupDay, $products, $pickupDayEntities); + break; + case $this->Cart::CART_TYPE_INSTANT_ORDER; + $actionLogType = 'instant_order_added'; + $userIdForActionLog = $this->getController()->getRequest()->getSession()->read('Auth.originalLoggedCustomer')['id_customer']; + if (empty($manufacturersThatReceivedInstantOrderNotification)) { + $message = __('Instant_order_({0})_successfully_placed_for_{1}.', [ + Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum()), + '' . $this->getController()->getRequest()->getSession()->read('Auth.orderCustomer')->name . '' + ]); + } else { + $message = __('Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}', [ + Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum()), + '' . $this->getController()->getRequest()->getSession()->read('Auth.orderCustomer')->name . '', + '' . join(', ', $manufacturersThatReceivedInstantOrderNotification) . '' + ]); + } + $message .= '
' . __('Pickup_day') . ': ' . Configure::read('app.timeHelper')->getDateFormattedWithWeekday(Configure::read('app.timeHelper')->getCurrentDay()).''; + $messageForActionLog = $message; + $cartGroupedByPickupDay = $this->Cart->getCartGroupedByPickupDay($cart); + if (!($this->AppAuth->isOrderForDifferentCustomerMode() && Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS'))) { + $this->sendConfirmationEmailToCustomer($cart, $cartGroupedByPickupDay, $products, []); + } + break; + case $this->Cart::CART_TYPE_SELF_SERVICE; + + if (Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && Configure::read('app.selfServiceModeAutoGenerateInvoice')) { + $this->Invoice = FactoryLocator::get('Table')->get('Invoices'); + $currentDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase(); + $invoiceData = $this->Invoice->getDataForCustomerInvoice($this->AppAuth->getUserId(), $currentDay); + + if (!$this->AppAuth->isOrderForDifferentCustomerMode()) { + $paidInCash = 0; + if ($this->AppAuth->isSelfServiceCustomer()) { + $paidInCash = 1; + } + if (Configure::read('appDb.FCS_HELLO_CASH_API_ENABLED')) { + $helloCash = new HelloCash(); + $responseObject = $helloCash->generateInvoice($invoiceData, $currentDay, $paidInCash, false); + $invoiceId = $responseObject->invoice_id; + $invoiceRoute = Configure::read('app.slugHelper')->getHelloCashReceipt($invoiceId); + } else { + $invoiceToCustomer = new GenerateInvoiceToCustomer(); + $newInvoice = $invoiceToCustomer->run($invoiceData, $currentDay, $paidInCash); + $invoiceId = $newInvoice->id; + $invoiceRoute = Configure::read('app.slugHelper')->getInvoiceDownloadRoute($newInvoice->filename); + } + $cart['invoice_id'] = $invoiceId; + } + } + + $actionLogType = 'self_service_order_added'; + $message = __('Thank_you_for_your_purchase!'); + $message .= '
'; + $message .= '
'.__('Sign_out').''; + $message .= ' '.__('Continue_shopping').''; + if (isset($invoiceRoute)) { + $message .= ' '.__('Print_receipt').''; + } + $messageForActionLog = __('{0}_has_placed_a_new_order_({1}).', [$this->AppAuth->getUsername(), Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum())]); + + if ($this->AppAuth->isOrderForDifferentCustomerMode()) { + $userIdForActionLog = $this->getController()->getRequest()->getSession()->read('Auth.originalLoggedCustomer')['id_customer']; + $messageForActionLog = __('{0}_has_placed_a_new_order_for_{1}_({2}).', [ + $this->getController()->getRequest()->getSession()->read('Auth.originalLoggedCustomer')['name'], + '' . $this->getController()->getRequest()->getSession()->read('Auth.orderCustomer')->name . '', + Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum()), + ]); + } else { + $this->sendConfirmationEmailToCustomerSelfService($cart, $products); + } + break; + } + + $this->ActionLog = FactoryLocator::get('Table')->get('ActionLogs'); + $this->ActionLog->customSave($actionLogType, $userIdForActionLog, $cart['Cart']->id_cart, 'carts', $messageForActionLog); + $this->getController()->Flash->success($message); + + return $cart; + } + public function finish() { @@ -190,24 +321,11 @@ public function finish() $stockAvailable2saveData = []; $stockAvailable2saveConditions = []; + $contain = $this->getProductContain(); + foreach ($this->getProducts() as $cartProduct) { $ids = $this->Product->getProductIdAndAttributeId($cartProduct['productId']); - - $contain = [ - 'Manufacturers', - 'Manufacturers.AddressManufacturers', - 'StockAvailables', - 'ProductAttributes.StockAvailables', - 'ProductAttributes.ProductAttributeCombinations', - 'Taxes', - ]; - if (Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED')) { - $contain[] = 'UnitProducts'; - $contain[] = 'PurchasePriceProducts.Taxes'; - $contain[] = 'ProductAttributes.PurchasePriceProductAttributes'; - $contain[] = 'ProductAttributes.UnitProductAttributes'; - } $product = $this->Product->find('all', [ 'conditions' => [ 'Products.id_product' => $ids['productId'] @@ -215,7 +333,7 @@ public function finish() 'contain' => $contain, ])->first(); - $product->next_delivery_day = $this->Product->getNextDeliveryDay($product, $this->AppAuth); + $product->next_delivery_day = DeliveryRhythm::getNextDeliveryDayForProduct($product, $this->AppAuth); $products[] = $product; $stockAvailableQuantity = $product->stock_available->quantity; @@ -304,7 +422,7 @@ public function finish() $cartErrors[$cartProduct['productId']][] = $message; } - if (!$this->AppAuth->isOrderForDifferentCustomerMode() && $product->next_delivery_day == 'delivery-rhythm-triggered-delivery-break') { + if (!$this->AppAuth->isOrderForDifferentCustomerMode() && !$this->AppAuth->isSelfServiceModeByUrl() && !$this->AppAuth->isSelfServiceModeByReferer() && $product->next_delivery_day == 'delivery-rhythm-triggered-delivery-break') { $message = __('{0}_can_be_ordered_next_week.', [ '' . $product->name . '' @@ -313,10 +431,16 @@ public function finish() $cartErrors[$cartProduct['productId']][] = $message; } - if (! $product->manufacturer->active || (!$this->AppAuth->isOrderForDifferentCustomerMode() && !$this->AppAuth->isSelfServiceModeByUrl() && $this->Product->deliveryBreakEnabled($product->manufacturer->no_delivery_days, $product->next_delivery_day))) { - $message = __('The_manufacturer_of_the_product_{0}_has_a_delivery_break_or_product_is_not_activated.', ['' . $product->name . '']); - $message .= ' ' . __('Please_delete_product_from_cart_to_place_order.'); - $cartErrors[$cartProduct['productId']][] = $message; + if (! $product->manufacturer->active || (!$this->AppAuth->isOrderForDifferentCustomerMode() + && !$this->AppAuth->isSelfServiceModeByUrl() + && $this->Product->deliveryBreakManufacturerEnabled( + $product->manufacturer->no_delivery_days, + $product->next_delivery_day, + $product->manufacturer->stock_management_enabled, + $product->is_stock_product))) { + $message = __('The_manufacturer_of_the_product_{0}_has_a_delivery_break_or_product_is_not_activated.', ['' . $product->name . '']); + $message .= ' ' . __('Please_delete_product_from_cart_to_place_order.'); + $cartErrors[$cartProduct['productId']][] = $message; } if (!$this->AppAuth->isOrderForDifferentCustomerMode()) { @@ -329,7 +453,7 @@ public function finish() } } - if (!$this->AppAuth->isOrderForDifferentCustomerMode() && !$this->AppAuth->isSelfServiceModeByUrl() && $this->Product->deliveryBreakEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $product->next_delivery_day)) { + if (!$this->AppAuth->isOrderForDifferentCustomerMode() && !$this->AppAuth->isSelfServiceModeByUrl() && $this->Product->deliveryBreakGlobalEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $product->next_delivery_day)) { $message = __('{0}_has_activated_the_delivery_break_and_product_{1}_cannot_be_ordered.', [ Configure::read('appDb.FCS_APP_NAME'), @@ -429,6 +553,7 @@ public function finish() $fixedPickupDayRequest[] = $pickupDay; } $this->getController()->setRequest($this->getController()->getRequest()->withData('Carts.pickup_day_entities', $fixedPickupDayRequest)); + $this->sendOrderCommentNotificationToPlatformOwner($pickupEntities); } } @@ -436,6 +561,11 @@ public function finish() $options['validate'] = 'customerCanSelectPickupDay'; } + if ($this->AppAuth->getCartType() == $this->Cart::CART_TYPE_SELF_SERVICE + && $this->AppAuth->isOrderForDifferentCustomerMode()) { + $options['validate'] = 'selfServiceForDifferentCustomer'; + } + $cart['Cart'] = $this->Cart->patchEntity( $cart['Cart'], $this->getController()->getRequest()->getData(), @@ -451,113 +581,10 @@ public function finish() if (!empty($cartErrors) || !empty($formErrors)) { $this->getController()->Flash->error(__('Errors_occurred.')); - } else { - - $this->saveOrderDetails($orderDetails2save); - $this->saveStockAvailable($stockAvailable2saveData, $stockAvailable2saveConditions); - - $manufacturersThatReceivedInstantOrderNotification = $this->sendInstantOrderNotificationToManufacturers($cart['CartProducts']); - $this->sendStockAvailableLimitReachedEmailToManufacturer($cart['Cart']->id_cart); - - $pickupDayEntities = null; - if (Configure::read('appDb.FCS_ORDER_COMMENT_ENABLED')) { - $pickupDayEntities = $cart['Cart']->pickup_day_entities; - $this->Cart->PickupDayEntities->saveMany($pickupDayEntities); - } - - $cart = $this->AppAuth->getCart(); // to get attached order details - $this->AppAuth->setCart($cart); - $cart['Cart'] = $this->markAsSaved(); // modified timestamp is needed later on! - - $cartType = $this->AppAuth->getCartType(); - $userIdForActionLog = $this->AppAuth->getUserId(); - - switch($cartType) { - case $this->Cart::CART_TYPE_WEEKLY_RHYTHM; - $actionLogType = 'customer_order_finished'; - $message = __('Your_order_has_been_placed_succesfully.'); - $messageForActionLog = __('{0}_has_placed_a_new_order_({1}).', [$this->AppAuth->getUsername(), Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum())]); - $cartGroupedByPickupDay = $this->Cart->getCartGroupedByPickupDay($cart, $customerSelectedPickupDay); - $this->sendConfirmationEmailToCustomer($cart, $cartGroupedByPickupDay, $products, $pickupDayEntities); - break; - case $this->Cart::CART_TYPE_INSTANT_ORDER; - $actionLogType = 'instant_order_added'; - $userIdForActionLog = $this->getController()->getRequest()->getSession()->read('Auth.originalLoggedCustomer')['id_customer']; - if (empty($manufacturersThatReceivedInstantOrderNotification)) { - $message = __('Instant_order_({0})_successfully_placed_for_{1}.', [ - Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum()), - '' . $this->getController()->getRequest()->getSession()->read('Auth.orderCustomer')->name . '' - ]); - } else { - $message = __('Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}', [ - Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum()), - '' . $this->getController()->getRequest()->getSession()->read('Auth.orderCustomer')->name . '', - '' . join(', ', $manufacturersThatReceivedInstantOrderNotification) . '' - ]); - } - $message .= '
' . __('Pickup_day') . ': ' . Configure::read('app.timeHelper')->getDateFormattedWithWeekday(Configure::read('app.timeHelper')->getCurrentDay()).''; - $messageForActionLog = $message; - $cartGroupedByPickupDay = $this->Cart->getCartGroupedByPickupDay($cart); - if (!($this->AppAuth->isOrderForDifferentCustomerMode() && Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS'))) { - $this->sendConfirmationEmailToCustomer($cart, $cartGroupedByPickupDay, $products, []); - } - break; - case $this->Cart::CART_TYPE_SELF_SERVICE; - - if (Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') && Configure::read('app.selfServiceModeAutoGenerateInvoice')) { - $this->Invoice = FactoryLocator::get('Table')->get('Invoices'); - $currentDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase(); - $invoiceData = $this->Invoice->getDataForCustomerInvoice($this->AppAuth->getUserId(), $currentDay); - - if (!$this->AppAuth->isOrderForDifferentCustomerMode()) { - $paidInCash = 0; - if ($this->AppAuth->isSelfServiceCustomer()) { - $paidInCash = 1; - } - if (Configure::read('appDb.FCS_HELLO_CASH_API_ENABLED')) { - $helloCash = new HelloCash(); - $responseObject = $helloCash->generateInvoice($invoiceData, $currentDay, $paidInCash, false); - $invoiceId = $responseObject->invoice_id; - $invoiceRoute = Configure::read('app.slugHelper')->getHelloCashReceipt($invoiceId); - } else { - $invoiceToCustomer = new GenerateInvoiceToCustomer(); - $newInvoice = $invoiceToCustomer->run($invoiceData, $currentDay, $paidInCash); - $invoiceId = $newInvoice->id; - $invoiceRoute = Configure::read('app.slugHelper')->getInvoiceDownloadRoute($newInvoice->filename); - } - $cart['invoice_id'] = $invoiceId; - } - } - - $actionLogType = 'self_service_order_added'; - $message = __('Thank_you_for_your_purchase!'); - $message .= '
'; - $message .= ' '.__('Sign_out').''; - $message .= ' '.__('Continue_shopping').''; - if (isset($invoiceRoute)) { - $message .= ' '.__('Print_receipt').''; - } - $messageForActionLog = __('{0}_has_placed_a_new_order_({1}).', [$this->AppAuth->getUsername(), Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum())]); - - if ($this->AppAuth->isOrderForDifferentCustomerMode()) { - $userIdForActionLog = $this->getController()->getRequest()->getSession()->read('Auth.originalLoggedCustomer')['id_customer']; - $messageForActionLog = __('{0}_has_placed_a_new_order_for_{1}_({2}).', [ - $this->getController()->getRequest()->getSession()->read('Auth.originalLoggedCustomer')['name'], - '' . $this->getController()->getRequest()->getSession()->read('Auth.orderCustomer')->name . '', - Configure::read('app.numberHelper')->formatAsCurrency($this->getProductSum()), - ]); - } else { - $this->sendConfirmationEmailToCustomerSelfService($cart, $products); - } - break; - } - - $this->ActionLog = FactoryLocator::get('Table')->get('ActionLogs'); - $this->ActionLog->customSave($actionLogType, $userIdForActionLog, $cart['Cart']->id_cart, 'carts', $messageForActionLog); - $this->getController()->Flash->success($message); - + return $cart; } + $cart = $this->saveCart($cart, $orderDetails2save, $stockAvailable2saveData, $stockAvailable2saveConditions, $customerSelectedPickupDay, $products); return $cart; } @@ -579,12 +606,12 @@ private function prepareOrderDetailPurchasePrices($ids, $product, $cartProduct) if ($attribute->id_product_attribute == $ids['attributeId']) { if (!empty($attribute->unit_product_attribute) && $attribute->unit_product_attribute->price_per_unit_enabled) { $totalPurchasePriceTaxIncl = $attribute->unit_product_attribute->purchase_price_incl_per_unit ?? 0; - $totalPurchasePriceTaxIncl = round($totalPurchasePriceTaxIncl * $cartProduct['productQuantityInUnits'] / $attribute->unit_product_attribute->amount, 2); + $totalPurchasePriceTaxIncl = round((float) $totalPurchasePriceTaxIncl * $cartProduct['productQuantityInUnits'] / $attribute->unit_product_attribute->amount, 2); $totalPurchasePriceTaxExcl = $this->Product->getNetPrice($totalPurchasePriceTaxIncl, $purchasePriceTaxRate); $totalPurchasePriceTaxExcl = round($totalPurchasePriceTaxExcl, 2); } else { $totalPurchasePriceTaxExcl = $attribute->purchase_price_product_attribute->price ?? 0; - $totalPurchasePriceTaxExcl = round($totalPurchasePriceTaxExcl, 2); + $totalPurchasePriceTaxExcl = round((float) $totalPurchasePriceTaxExcl, 2); $totalPurchasePriceTaxIncl = $this->Product->getGrossPrice($totalPurchasePriceTaxExcl, $purchasePriceTaxRate); $totalPurchasePriceTaxIncl *= $amount; $totalPurchasePriceTaxExcl *= $amount; @@ -596,12 +623,12 @@ private function prepareOrderDetailPurchasePrices($ids, $product, $cartProduct) // main product if (!empty($product->unit_product) && $product->unit_product->price_per_unit_enabled) { $totalPurchasePriceTaxIncl = $product->unit_product->purchase_price_incl_per_unit ?? 0; - $totalPurchasePriceTaxIncl = round($totalPurchasePriceTaxIncl * $cartProduct['productQuantityInUnits'] / $product->unit_product->amount, 2); + $totalPurchasePriceTaxIncl = round((float) $totalPurchasePriceTaxIncl * $cartProduct['productQuantityInUnits'] / $product->unit_product->amount, 2); $totalPurchasePriceTaxExcl = $this->Product->getNetPrice($totalPurchasePriceTaxIncl, $purchasePriceTaxRate); $totalPurchasePriceTaxExcl = round($totalPurchasePriceTaxExcl, 2); } else { $totalPurchasePriceTaxExcl = $product->purchase_price_product->price ?? 0; - $totalPurchasePriceTaxExcl = round($totalPurchasePriceTaxExcl, 2); + $totalPurchasePriceTaxExcl = round((float) $totalPurchasePriceTaxExcl, 2); $totalPurchasePriceTaxIncl = $this->Product->getGrossPrice($totalPurchasePriceTaxExcl, $purchasePriceTaxRate); $totalPurchasePriceTaxIncl *= $amount; $totalPurchasePriceTaxExcl *= $amount; @@ -780,7 +807,7 @@ private function sendStockAvailableLimitReachedEmailToManufacturer($cartId) 'stockAvailable' => $stockAvailable, 'manufacturer' => $cartProduct->product->manufacturer, 'showManufacturerName' => true, - 'notificationEditLink' => __('You_can_unsubscribe_this_email_in_the_settings_of_the_manufacturer.', [Configure::read('app.cakeServerName') . Configure::read('app.slugHelper')->getManufacturerEditOptions($cartProduct->product->id_manufacturer)]) + 'notificationEditLink' => __('You_can_unsubscribe_this_email_in_the_settings_of_the_manufacturer.', [Configure::read('App.fullBaseUrl') . Configure::read('app.slugHelper')->getManufacturerEditOptions($cartProduct->product->id_manufacturer)]) ]); $email->addToQueue(); } @@ -797,11 +824,38 @@ private function sendConfirmationEmailToCustomerSelfService($cart, $products) ->setSubject(__('Your_purchase')) ->setViewVars([ 'cart' => $this->Cart->getCartGroupedByPickupDay($cart), - 'appAuth' => $this->AppAuth + 'appAuth' => $this->AppAuth, ]); $email->addToQueue(); } + private function sendOrderCommentNotificationToPlatformOwner($pickupDayEntities) + { + if (!Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS') || Configure::read('appDb.FCS_APP_EMAIL') == '') { + return false; + } + foreach($pickupDayEntities as $pickupDay) { + if ($pickupDay['comment'] == '') { + continue; + } + $formattedPickupDay = FrozenDate::createFromFormat(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), $pickupDay['pickup_day']); + $formattedPickupDay = $formattedPickupDay->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateLong2')); + $email = new AppMailer(); + $email->viewBuilder()->setTemplate('order_comment_notification'); + $email->setTo(Configure::read('appDb.FCS_APP_EMAIL')) + ->setSubject(__('New_order_comment__was_written_by_{0}_for_{1}', [ + $this->AppAuth->getUsername(), + $formattedPickupDay, + ])) + ->setViewVars([ + 'comment' => $pickupDay['comment'], + 'formattedPickupDay' => $formattedPickupDay, + 'appAuth' => $this->AppAuth, + ]); + $email->addToQueue(); + } + } + /** * does not send email to inactive users (superadmins can place instant orders for inactive users!) */ diff --git a/src/Controller/Component/SanitizeComponent.php b/src/Controller/Component/SanitizeComponent.php index e06e279f8a..eb64d2e616 100644 --- a/src/Controller/Component/SanitizeComponent.php +++ b/src/Controller/Component/SanitizeComponent.php @@ -1,4 +1,6 @@ $atPos) { + $key = str_shuffle($character_set); + $atPos = strpos($key, '@'); + $dotPos = strpos($key, '.'); + } + $cipher_text = ''; $id = 'e' . rand(1, 999999999); for ($i = 0; $i < strlen($email); $i += 1) { diff --git a/src/Controller/CronController.php b/src/Controller/CronController.php index f884832205..9709278f52 100644 --- a/src/Controller/CronController.php +++ b/src/Controller/CronController.php @@ -1,4 +1,5 @@ addAttachments([__d('admin', 'Filename_Terms-of-use').'.pdf' => ['data' => $this->generateTermsOfUsePdf($customer), 'mimetype' => 'application/pdf']]); + $email->addAttachments([__('Filename_Terms-of-use').'.pdf' => ['data' => $this->generateTermsOfUsePdf($customer), 'mimetype' => 'application/pdf']]); } $email->addToQueue(); @@ -284,7 +285,12 @@ public function login() $this->set('title_for_layout', $title); - /** + if ($this->getRequest()->is('post')) { + // no spam protected email output in input field when login or registration fails + $this->protectEmailAddresses = false; + } + + /** * login start */ if ($this->getRequest()->getUri()->getPath() == Configure::read('app.slugHelper')->getLogin()) { @@ -293,6 +299,7 @@ public function login() } if ($this->getRequest()->is('post')) { + $customer = $this->AppAuth->identify(); if ($customer) { $this->AppAuth->setUser($customer); @@ -306,7 +313,7 @@ public function login() !empty($customer)) { $customer = $this->Customer->get($customer['id_customer']); if ($customer->auto_login_hash == '') { - $customer->auto_login_hash = Security::hash(rand()); + $customer->auto_login_hash = Security::hash((string) rand()); $this->Customer->save($customer); } $cookie = (new Cookie('remember_me')) diff --git a/src/Controller/ErrorController.php b/src/Controller/ErrorController.php index a5c5e0299f..d783b76da4 100644 --- a/src/Controller/ErrorController.php +++ b/src/Controller/ErrorController.php @@ -1,4 +1,5 @@ __('This_page_uses_cookies'), 'CookiesExplainationText' => __('Cookies_explaination_text'), 'AcceptCookies' => __('Accept_cookies'), - 'YouHaveAlredyOrdered01TimesFor2' => __('You_have_already_ordered_{0}_{1}_times_for_{2}.'), + 'YouHaveAlreadyOrdered01TimesFor2' => __('You_have_already_ordered_{0}_{1}_times_for_{2}.'), 'Firstname' => __('Firstname'), 'Lastname' => __('Lastname'), 'ContactPerson' => __('Contact_person'), @@ -144,8 +146,9 @@ private function getStrings() 'DeliveredTotalWeight' => __('Delivered_total_weight'), 'BasePrice' => __('Base_price'), 'PriceIsAutomaticallyAdaptedAfterSave' => __('Price_is_automatically_adapted_after_save.'), - 'FieldIsRedIfWeightNotYetAdapted' => __('The_field_is_red_if_weight_not_yet_adapted.'), 'DoNotAutomaticallyAdaptPriceJustChangeWeight' => __('Do_not_automatically_apapt_price_just_change_weight.'), + 'Calculator' => __('Calculator'), + 'ExampleGivenAbbr' => __('Example_given_abbr'), 'ReallyDeleteOrderedProduct' => __('Really_delete_ordered_product?'), 'ProductCancellation' => __('Product_cancellation'), 'DoYouReallyWantToCancelProduct0' => __('Do_you_really_want_to_cancel_product_{0}?'), @@ -154,10 +157,8 @@ private function getStrings() 'WhyAreProductsCancelled' => __('Why_are_products_cancelled_(mandatory_field)?'), 'YesDoCancelButton' => __('Yes_do_cancel_button!'), 'PleaseOnlyCancelIfOkForManufacturer' => __('Please_only_cancel_if_ok_for_manufacturer!'), - 'ReallyDeleteSelectedProduct' => __('Really_delete_selected_product?'), - 'ReallyDeleteSelectedProducts' => __('Really_delete_selected_products?'), - 'YouSelectedOneProduct' => __('You_selected_1_product'), - 'YouSelected0Products' => __('You_selected_{0}_products'), + 'YouSelectedOneProduct' => __('You_selected_1_product.'), + 'YouSelected0Products' => __('You_selected_{0}_products.'), 'AdaptAmountReasonIsMandatory' => __('Adapt_amount_reason_is_mandatory.'), 'AdaptPriceReasonIsMandatory' => __('Adapt_price_reason_is_mandatory.'), 'CancellationReasonIsMandatory' => __('Cancellation_reason_is_mandatory.'), @@ -176,6 +177,8 @@ private function getStrings() 'DoNotShowProductAsNew' => __('Do_not_show_product_as_new?'), 'ReallyShowProduct0AsNew' => __('Really_show_product_{0}_as_new?'), 'ReallyDoNotShowProduct0AsNew' => __('Really_do_not_show_product_{0}_as_new?'), + 'Activate' => __('Activate'), + 'Deactivate' => __('Deactivate'), 'ActivateProduct' => __('Activate_product'), 'DeactivateProduct' => __('Deactivate_product'), 'ActivateMember' => __('Activate_member?'), @@ -200,6 +203,7 @@ private function getStrings() 'ReallyDeleteMember' => __('Really_delete_member?'), 'BeCarefulNoWayBack' => __('Be_careful_there_is_no_way_back!'), 'ErrorsOccurredWhileMemberWasDeleted' => __('Errors_occurred_while_member_was_deleted'), + 'ErrorsOccurredWhileProductStatusWasChanged' => __('Errors_occurred_while_product_status_was_changed'), 'DeleteProducts' => __('Delete_products?'), 'ReallyDelete0Products' => __('Really_delete_{0}_products?'), 'ErrorsOccurredWhileProductsWereDeleted' => __('Errors_occurred_while_products_were_deleted'), @@ -253,6 +257,8 @@ private function getStrings() 'GivenAmount' => __('Given_amount'), 'back' => __('back'), 'SendEmailToMember' => __('Send_email_to_member'), + 'SendEmailToBothMembers' => __('Send_email_to_both_members'), + 'ChangeStatus' => __('Change_status'), ], 'pickupDay' => [ 'WereTheProductsPickedUp' => __('Were_the_products_picked_up?'), @@ -375,7 +381,7 @@ public function afterFilter(EventInterface $event) { parent::afterFilter($event); if (Configure::check('app.outputStringReplacements')) { - $newOutput = OutputFilter::replace($this->response->getBody(), Configure::read('app.outputStringReplacements')); + $newOutput = OutputFilter::replace($this->response->getBody()->__toString(), Configure::read('app.outputStringReplacements')); $this->response = $this->response->withStringBody($newOutput); } } diff --git a/src/Controller/ManufacturersController.php b/src/Controller/ManufacturersController.php index 5bd5898db6..c3d6b8bcfe 100644 --- a/src/Controller/ManufacturersController.php +++ b/src/Controller/ManufacturersController.php @@ -1,4 +1,5 @@ Please copy this Security => salt to your custom_config.php: '.hash('sha256', Security::randomBytes(64)).'

'; $securityErrors++; } - if (Configure::read('app.cakeServerName') == '') { - echo '

Please copy http://' . $_SERVER['HTTP_HOST'] . ' to custom_config.php

'; + if (Configure::read('App.fullBaseUrl') == '') { + echo '

Please copy ' . $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . ' to custom_config.php

'; $securityErrors++; } if ($securityErrors > 0) { diff --git a/src/Controller/ProductsController.php b/src/Controller/ProductsController.php index 087a5030b2..f0ac6c90ea 100644 --- a/src/Controller/ProductsController.php +++ b/src/Controller/ProductsController.php @@ -1,4 +1,5 @@ getQuery($appAuth, $categoryId, $filterByNewProducts, $keyword, $productId, $countMode, $getOnlyStockProducts, $manufacturerId); + $query = $this->getQuery($appAuth, $categoryId, $filterByNewProducts, $keyword, $productId, $getOnlyStockProducts, $manufacturerId); $products = $query->toArray(); $products = $this->hideProductsWithActivatedDeliveryRhythmOrDeliveryBreak($appAuth, $products); $products = $this->removeProductIfAllAttributesRemovedDueToNoPurchasePrice($products); + $products = $this->addOrderedProductsTotalAmount($products, $appAuth); Cache::write($cacheKey, $products); } @@ -67,7 +74,7 @@ public function getProductsByManufacturerId($appAuth, $manufacturerId, $countMod return $this->getProducts($appAuth, '', false, '', 0, $countMode, false, $manufacturerId); } - protected function getQuery($appAuth, $categoryId, $filterByNewProducts = false, $keyword = '', $productId = 0, $countMode = false, $getOnlyStockProducts = false, $manufacturerId = 0) + protected function getQuery($appAuth, $categoryId, $filterByNewProducts, $keyword, $productId, $getOnlyStockProducts, $manufacturerId) { $this->Product = FactoryLocator::get('Table')->get('Products'); @@ -332,9 +339,13 @@ protected function addKeywordFilter($query, $keyword) } $query->where(function (QueryExpression $exp, Query $q) use($keyword) { + $searchValue = '%' . $keyword . '%'; + if (I18n::getLocale() == 'de_DE') { + $searchValue = new StringExpression('%'.$keyword.'%', 'utf8mb4_german2_ci'); + } $or = [ - $q->newExpr()->like('Products.name', '%'.$keyword.'%'), - $q->newExpr()->like('Products.description_short', '%'.$keyword.'%'), + $q->newExpr()->like('Products.name', $searchValue), + $q->newExpr()->like('Products.description_short', $searchValue), $q->newExpr()->eq('Products.id_product', (int) $keyword), ]; if (Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED')) { @@ -356,6 +367,41 @@ protected function addKeywordFilter($query, $keyword) } + protected function addOrderedProductsTotalAmount($products, $appAuth) + { + + if (!Configure::read('app.showOrderedProductsTotalAmountInCatalog')) { + return $products; + } + + if (!$appAuth->user()) { + return $products; + } + + if ($appAuth->isOrderForDifferentCustomerMode() || $appAuth->isSelfServiceModeByUrl()) { + return $products; + } + + $this->OrderDetail = FactoryLocator::get('Table')->get('OrderDetails'); + + $i = -1; + foreach($products as $product) { + $i++; + $pickupDay = DeliveryRhythm::getNextDeliveryDayForProduct($product, $appAuth); + if (empty($product->product_attributes)) { + $product->ordered_total_amount = $this->OrderDetail->getTotalOrderDetails($pickupDay, $product->id_product, 0); + } else { + foreach($product->product_attributes as &$attribute) { + $attribute->ordered_total_amount = $this->OrderDetail->getTotalOrderDetails($pickupDay, $product->id_product, $attribute->id_product_attribute); + } + } + } + + return $products; + + } + + protected function removeProductIfAllAttributesRemovedDueToNoPurchasePrice($products) { if (!Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED')) { @@ -392,7 +438,7 @@ protected function hideProductsWithActivatedDeliveryRhythmOrDeliveryBreak($appAu $i = -1; foreach($products as $product) { $i++; - $deliveryDate = $this->Product->calculatePickupDayRespectingDeliveryRhythm($product); + $deliveryDate = DeliveryRhythm::getNextPickupDayForProduct($product); // deactivates the product if it can not be ordered this week if ($deliveryDate == 'delivery-rhythm-triggered-delivery-break') { @@ -400,12 +446,16 @@ protected function hideProductsWithActivatedDeliveryRhythmOrDeliveryBreak($appAu } // deactivates the product if manufacturer based delivery break is enabled - if ($this->Product->deliveryBreakEnabled($product->manufacturer->no_delivery_days, $deliveryDate)) { - $products[$i]->delivery_break_enabled = true; + if ($this->Product->deliveryBreakManufacturerEnabled( + $product->manufacturer->no_delivery_days, + $deliveryDate, + $product->manufacturer->stock_management_enabled, + $product->is_stock_product)) { + $products[$i]->delivery_break_enabled = true; } // deactivates the product if global delivery break is enabled - if ($this->Product->deliveryBreakEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $deliveryDate)) { + if ($this->Product->deliveryBreakGlobalEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $deliveryDate)) { $products[$i]->delivery_break_enabled = true; } @@ -484,7 +534,7 @@ public function prepareProducts($appAuth, $products) $products[$i]->deposit_product->deposit = 0; } - $products[$i]->next_delivery_day = $this->Product->getNextDeliveryDay($product, $appAuth); + $products[$i]->next_delivery_day = DeliveryRhythm::getNextDeliveryDayForProduct($product, $appAuth); foreach ($product->product_attributes as &$attribute) { diff --git a/src/Lib/Csv/BankingReader.php b/src/Lib/Csv/BankingReader.php index e61afd4995..32b6692061 100644 --- a/src/Lib/Csv/BankingReader.php +++ b/src/Lib/Csv/BankingReader.php @@ -1,4 +1,6 @@ checkStructureForRecord($record); } - return $structureIsOk; + return (bool) $structureIsOk; } } diff --git a/src/Lib/Csv/BankingReaderInterface.php b/src/Lib/Csv/BankingReaderInterface.php index 41acac36b4..77d62d7ae1 100644 --- a/src/Lib/Csv/BankingReaderInterface.php +++ b/src/Lib/Csv/BankingReaderInterface.php @@ -1,4 +1,6 @@ \ No newline at end of file diff --git a/src/Lib/Csv/RaiffeisenBankingReader.php b/src/Lib/Csv/RaiffeisenBankingReader.php index 0df190edbe..18fafb72c8 100644 --- a/src/Lib/Csv/RaiffeisenBankingReader.php +++ b/src/Lib/Csv/RaiffeisenBankingReader.php @@ -1,4 +1,6 @@ \ No newline at end of file diff --git a/src/Lib/Csv/SparkasseBankingReader.php b/src/Lib/Csv/SparkasseBankingReader.php index 4b0d7141a5..187eb0e83f 100644 --- a/src/Lib/Csv/SparkasseBankingReader.php +++ b/src/Lib/Csv/SparkasseBankingReader.php @@ -1,4 +1,6 @@ \ No newline at end of file diff --git a/src/Lib/Csv/VolksbankBankingReader.php b/src/Lib/Csv/VolksbankBankingReader.php index 913d2e6651..fcf8ac06b7 100644 --- a/src/Lib/Csv/VolksbankBankingReader.php +++ b/src/Lib/Csv/VolksbankBankingReader.php @@ -1,4 +1,6 @@ \ No newline at end of file diff --git a/src/Lib/DeliveryNote/GenerateDeliveryNote.php b/src/Lib/DeliveryNote/GenerateDeliveryNote.php index ccc98343ca..507b2014be 100644 --- a/src/Lib/DeliveryNote/GenerateDeliveryNote.php +++ b/src/Lib/DeliveryNote/GenerateDeliveryNote.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ +namespace App\Lib\DeliveryRhythm; + +use Cake\Core\Configure; +use Cake\I18n\I18n; + +class DeliveryRhythm +{ + + public static function getSendOrderListsWeekday() + { + $sendOrderListsWeekday = Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY') - Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA'); + if ($sendOrderListsWeekday < 0) { + $sendOrderListsWeekday += 7; + } + return $sendOrderListsWeekday; + } + + public static function getDeliveryDateByCurrentDayForDb() + { + $deliveryDate = self::getDeliveryDayByCurrentDay(); + $deliveryDate = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), $deliveryDate); + return $deliveryDate; + } + + public static function getNextWeeklyDeliveryDays($maxDays=52) + { + $nextDeliveryDay = self::getDeliveryDateByCurrentDayForDb(); + return Configure::read('app.timeHelper')->getWeekdayFormattedDaysList($nextDeliveryDay, $maxDays, 7); + } + + public static function getNextDailyDeliveryDays($maxDays) + { + $nextDeliveryDay = Configure::read('app.timeHelper')->getTomorrowForDatabase(); + return Configure::read('app.timeHelper')->getWeekdayFormattedDaysList($nextDeliveryDay, $maxDays, 1); + } + + public static function getDeliveryDayByCurrentDay() + { + return self::getDeliveryDay(Configure::read('app.timeHelper')->getCurrentDay()); + } + + public static function getDeliveryWeekday() + { + return Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY'); + } + + public static function getLastOrderDay($nextDeliveryDay, $deliveryRhythmType, $deliveryRhythmCount, $deliveryRhythmSendOrderListWeekday, $deliveryRhythmOrderPossibleUntil) + { + + if ($nextDeliveryDay == 'delivery-rhythm-triggered-delivery-break') { + return ''; + } + + if ($deliveryRhythmType == 'individual') { + $result = strtotime($deliveryRhythmOrderPossibleUntil->i18nFormat(Configure::read('DateFormat.Database'))); + } else { + $lastOrderWeekday = Configure::read('app.timeHelper')->getNthWeekdayBeforeWeekday(1, $deliveryRhythmSendOrderListWeekday); + $tmpLocale = I18n::getLocale(); + I18n::setLocale('en_US'); + $weekdayAsNameInEnglish = Configure::read('app.timeHelper')->getWeekdayName($lastOrderWeekday); + I18n::setLocale($tmpLocale); + $result = strtotime('last ' . $weekdayAsNameInEnglish, strtotime($nextDeliveryDay)); + } + + $result = date(Configure::read('DateFormat.DatabaseAlt'), $result); + return $result; + + } + + public static function getDbFormattedPickupDayByDbFormattedDate($date, $sendOrderListsWeekday = null, $deliveryRhythmType = null, $deliveryRhythmCount = null) + { + if (is_null($sendOrderListsWeekday)) { + $sendOrderListsWeekday = self::getSendOrderListsWeekday(); + } + $pickupDay = self::getDeliveryDay(strtotime($date), $sendOrderListsWeekday, $deliveryRhythmType, $deliveryRhythmCount); + $pickupDay = date(Configure::read('DateFormat.DatabaseAlt'), $pickupDay); + return $pickupDay; + } + + public static function getFormattedNextDeliveryDay($day) + { + return date(Configure::read('app.timeHelper')->getI18Format('DateShortAlt'), strtotime(self::getNextDeliveryDay($day))); + } + + public static function getOrderPeriodFirstDayByDeliveryDay(int $deliveryDay) + { + if (self::hasSaturdayThursdayConfig()) { + $deliveryDay = strtotime('-7 days', $deliveryDay); + } + return self::getOrderPeriodFirstDay($deliveryDay); + } + + public static function getOrderPeriodLastDayByDeliveryDay(int $deliveryDay) + { + if (self::hasSaturdayThursdayConfig()) { + $deliveryDay = strtotime('-7 days', $deliveryDay); + } + return self::getOrderPeriodLastDay($deliveryDay); + } + + public static function getNextDeliveryDay($day) + { + $orderPeriodFirstDay = self::getOrderPeriodFirstDay($day); + $deliveryDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), self::getDeliveryDay(strtotime($orderPeriodFirstDay))); + if (self::hasSaturdayThursdayConfig()) { + $deliveryDay = date( + Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), + strtotime($deliveryDay . '-7 days'), + ); + } + return $deliveryDay; + } + + public static function getDeliveryDay($orderDay, $sendOrderListsWeekday = null, $deliveryRhythmType = null, $deliveryRhythmCount = null) + { + if (is_null($deliveryRhythmType)) { + $deliveryRhythmType = 'week'; + } + if (is_null($deliveryRhythmCount)) { + $deliveryRhythmCount = 1; + } + + if (is_null($sendOrderListsWeekday)) { + $sendOrderListsWeekday = self::getSendOrderListsWeekday(); + } + + $daysToAddToOrderPeriodLastDay = self::getDaysToAddToOrderPeriodLastDay(); + $deliveryDate = strtotime(self::getOrderPeriodLastDay($orderDay) . '+' . $daysToAddToOrderPeriodLastDay . ' days'); + + $weekdayOrderDay = Configure::read('app.timeHelper')->formatAsWeekday($orderDay); + $weekdayOrderDay = $weekdayOrderDay % 7; + + $weekdayDeliveryDate = Configure::read('app.timeHelper')->formatAsWeekday($deliveryDate); + $weekdayStringDeliveryDate = strtolower(date('l', $deliveryDate)); + + if (self::hasSaturdayThursdayConfig()) { + $calculateNextDeliveryDay = $weekdayOrderDay == 6 || ( + $weekdayOrderDay == 5 && $sendOrderListsWeekday == 5 + ); + } else { + $calculateNextDeliveryDay = $weekdayOrderDay >= $sendOrderListsWeekday + && $weekdayOrderDay <= $weekdayDeliveryDate; + } + + if ($calculateNextDeliveryDay && $deliveryRhythmType != 'individual') { + $preparedOrderDay = date(Configure::read('app.timeHelper')->getI18Format('DateShortAlt'), $orderDay); + $deliveryDate = strtotime($preparedOrderDay . '+ ' . $deliveryRhythmCount . ' ' . $deliveryRhythmType . ' ' . $weekdayStringDeliveryDate); + } + + return $deliveryDate; + } + + public static function getOrderPeriodFirstDay($day) + { + + $currentWeekday = Configure::read('app.timeHelper')->formatAsWeekday($day); + $dateDiff = 7 - self::getSendOrderListsWeekday() + $currentWeekday; + $date = strtotime('-' . $dateDiff . ' day ', $day); + + if (self::hasSaturdayThursdayConfig()) { + $addOneWeekCondition = in_array($currentWeekday, [6,7]) && self::getDeliveryWeekday(); + } else { + $addOneWeekCondition = $currentWeekday > self::getDeliveryWeekday(); + } + + if ($addOneWeekCondition) { + $date = strtotime('+7 day', $date); + } + + $date = date(Configure::read('app.timeHelper')->getI18Format('DateShortAlt'), $date); + + return $date; + } + + public static function getOrderPeriodLastDay($day) + { + + $currentWeekday = Configure::read('app.timeHelper')->formatAsWeekday($day); + + if ($currentWeekday == 7) { + $currentWeekday = 0; + } + + if ($currentWeekday == self::getDeliveryWeekday()) { + $dateDiff = -1 - Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA'); + } + if ($currentWeekday == (self::getDeliveryWeekday() + 1) % 7) { + $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 5; + } + if ($currentWeekday == (self::getDeliveryWeekday() + 2) % 7) { + $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 4; + } + if ($currentWeekday == (self::getDeliveryWeekday() + 3) % 7) { + $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 3; + } + if ($currentWeekday == (self::getDeliveryWeekday() + 4) % 7) { + $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 2; + } + if ($currentWeekday == (self::getDeliveryWeekday() + 5) % 7) { + $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 1; + } + if ($currentWeekday == (self::getDeliveryWeekday() + 6) % 7) { + $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1); + } + + if (self::hasSaturdayThursdayConfig() && $dateDiff < 0) { + $dateDiff += 7; + } + + $date = date(Configure::read('app.timeHelper')->getI18Format('DateShortAlt'), strtotime($dateDiff . ' day ', $day)); + + + return $date; + } + + public static function getNextDeliveryDayForProduct($product, $appAuth) + { + if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { + $nextDeliveryDay = '1970-01-01'; + } elseif ($appAuth->isOrderForDifferentCustomerMode() || $appAuth->isSelfServiceModeByUrl()) { + $nextDeliveryDay = Configure::read('app.timeHelper')->getCurrentDateForDatabase(); + } else { + $nextDeliveryDay = self::getNextPickupDayForProduct($product); + } + return $nextDeliveryDay; + } + + public static function getNextPickupDayForProduct($product, $currentDay=null) + { + + if (is_null($currentDay)) { + $currentDay = Configure::read('app.timeHelper')->getCurrentDateForDatabase(); + } + + $sendOrderListsWeekday = null; + if (!is_null($product->delivery_rhythm_send_order_list_weekday)) { + $sendOrderListsWeekday = $product->delivery_rhythm_send_order_list_weekday; + } + + $pickupDay = self::getDbFormattedPickupDayByDbFormattedDate($currentDay, $sendOrderListsWeekday); + + // assure that $product->is_stock_product also contains check for $product->manufacturer->stock_management_enabled + if ($product->is_stock_product) { + return $pickupDay; + } + + if (Configure::read('appDb.FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY')) { + if ($product->delivery_rhythm_type == 'week' && $product->delivery_rhythm_count == 1) { + $regularPickupDay = self::getDbFormattedPickupDayByDbFormattedDate($currentDay); + if ($pickupDay != $regularPickupDay) { + return 'delivery-rhythm-triggered-delivery-break'; + } + } + } + + if ($product->delivery_rhythm_type == 'week') { + if (!is_null($product->delivery_rhythm_first_delivery_day)) { + $calculatedPickupDay = $product->delivery_rhythm_first_delivery_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); + while($calculatedPickupDay < $pickupDay) { + $calculatedPickupDay = strtotime($calculatedPickupDay . '+' . $product->delivery_rhythm_count . ' week'); + $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), $calculatedPickupDay); + } + + if (Configure::read('appDb.FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY')) { + if (in_array($product->delivery_rhythm_count, [1, 2]) && $pickupDay != $calculatedPickupDay) { + return 'delivery-rhythm-triggered-delivery-break'; + } + } + + $pickupDay = $calculatedPickupDay; + } + } + + if ($product->delivery_rhythm_type == 'month') { + $ordinal = match($product->delivery_rhythm_count) { + 1 => 'first', + 2 => 'second', + 3 => 'third', + 4 => 'fourth', + 0 => 'last', + }; + $deliveryDayAsWeekdayInEnglish = strtolower(date('l', strtotime($pickupDay))); + $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), strtotime($currentDay . ' ' . $ordinal . ' ' . $deliveryDayAsWeekdayInEnglish . ' of this month')); + + if (!is_null($product->delivery_rhythm_first_delivery_day)) { + $calculatedPickupDay = $product->delivery_rhythm_first_delivery_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); + } + + while($calculatedPickupDay < $pickupDay) { + $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), strtotime($calculatedPickupDay . ' ' . $ordinal . ' ' . $deliveryDayAsWeekdayInEnglish . ' of next month')); + } + $pickupDay = $calculatedPickupDay; + } + + if ($product->delivery_rhythm_type == 'individual') { + $pickupDay = $product->delivery_rhythm_first_delivery_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); + } + + return $pickupDay; + + } + + public static function getDaysToAddToOrderPeriodLastDay() + { + return Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') + 1; + } + + public static function hasSaturdayThursdayConfig() + { + return self::compareConfig(4, 5); + } + + private static function compareConfig($weeklyPickupDay, $defaultSendOrderListsDayDelta) + { + return Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY') == $weeklyPickupDay && + Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') == $defaultSendOrderListsDayDelta; + } + +} diff --git a/src/Lib/Error/Exception/ConfigFileMissingException.php b/src/Lib/Error/Exception/ConfigFileMissingException.php index 2645227ac7..6b982a11a4 100644 --- a/src/Lib/Error/Exception/ConfigFileMissingException.php +++ b/src/Lib/Error/Exception/ConfigFileMissingException.php @@ -1,4 +1,6 @@ customerEmail = $customer->email; $sendInvoiceToCustomer->invoicePdfFile = ''; $sendInvoiceToCustomer->invoiceNumber = $invoice->invoice_number; + $sendInvoiceToCustomer->invoiceSumPriceIncl = $invoice->sumPriceIncl; $sendInvoiceToCustomer->invoiceDate = $invoice->created->i18nFormat(Configure::read('app.timeHelper')->getI18Format('DateLong2')); $sendInvoiceToCustomer->invoiceId = $invoice->id; $sendInvoiceToCustomer->originalInvoiceId = $invoice->original_invoice_id ?? null; diff --git a/src/Lib/HelloCash/HelloCashApiException.php b/src/Lib/HelloCash/HelloCashApiException.php index 3aadd4447f..7a4c508971 100644 --- a/src/Lib/HelloCash/HelloCashApiException.php +++ b/src/Lib/HelloCash/HelloCashApiException.php @@ -1,4 +1,6 @@ customerEmail = $data->email; $sendInvoiceToCustomer->invoicePdfFile = $invoicePdfFile; $sendInvoiceToCustomer->invoiceNumber = $invoiceNumber; + $sendInvoiceToCustomer->invoiceSumPriceIncl = $data->sumPriceIncl; $sendInvoiceToCustomer->invoiceDate = $invoiceDate; $sendInvoiceToCustomer->invoiceId = $newInvoice->id; $sendInvoiceToCustomer->originalInvoiceId = null; diff --git a/src/Lib/Invoice/SendInvoiceToCustomer.php b/src/Lib/Invoice/SendInvoiceToCustomer.php index 3f3debdfb2..99022599a1 100644 --- a/src/Lib/Invoice/SendInvoiceToCustomer.php +++ b/src/Lib/Invoice/SendInvoiceToCustomer.php @@ -1,4 +1,6 @@ invoiceNumber; $invoiceDate = $this->invoiceDate; $invoiceId = $this->invoiceId; + $invoiceSumPriceIncl = $this->invoiceSumPriceIncl; $paidInCash = $this->paidInCash; $isCancellationInvoice = (bool) $this->isCancellationInvoice; $originalInvoiceId = $this->originalInvoiceId ?? $invoiceId; @@ -59,6 +63,7 @@ public function run() ->setSubject($subject) ->setViewVars([ 'paidInCash' => $paidInCash, + 'invoiceSumPriceIncl' => $invoiceSumPriceIncl, 'customerName' => $customerName, 'creditBalance' => $creditBalance, ]); diff --git a/src/Lib/OutputFilter/OutputFilter.php b/src/Lib/OutputFilter/OutputFilter.php index a36c273557..bcc3971c41 100644 --- a/src/Lib/OutputFilter/OutputFilter.php +++ b/src/Lib/OutputFilter/OutputFilter.php @@ -1,4 +1,6 @@ 4 ]; + public $html = ''; + public function __construct($orientation = 'P', $unit = 'mm', $format = 'A4', $unicode = true, $encoding = 'UTF-8', $diskcache = false, $pdfa = false) { parent::__construct($orientation, $unit, $format, $unicode, $encoding, $diskcache, $pdfa); diff --git a/src/Lib/Pdf/CustomerInvoiceBaseTcpdf.php b/src/Lib/Pdf/CustomerInvoiceBaseTcpdf.php index cf26b42456..5cba7eab03 100644 --- a/src/Lib/Pdf/CustomerInvoiceBaseTcpdf.php +++ b/src/Lib/Pdf/CustomerInvoiceBaseTcpdf.php @@ -1,4 +1,6 @@ Manufacturer = FactoryLocator::get('Table')->get('Manufacturers'); } - public function prepareAndSetData($manufacturerId, $dateFrom, $dateTo, $newInvoiceNumber, $validOrderStates, $period, $invoiceDate) + public function prepareAndSetData($manufacturerId, $dateFrom, $dateTo, $newInvoiceNumber, $validOrderStates, $period, $invoiceDate, $isAnonymized) { $manufacturer = $this->Manufacturer->find('all', [ @@ -45,7 +47,14 @@ public function prepareAndSetData($manufacturerId, $dateFrom, $dateTo, $newInvoi ])->first(); $productResults = $this->Manufacturer->getDataForInvoiceOrOrderList($manufacturerId, 'product', $dateFrom, $dateTo, $validOrderStates, Configure::read('appDb.FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES')); + if ($isAnonymized) { + $productResults = $this->Manufacturer->anonymizeCustomersInInvoiceOrOrderList($productResults); + } + $customerResults = $this->Manufacturer->getDataForInvoiceOrOrderList($manufacturerId, 'customer', $dateFrom, $dateTo, $validOrderStates, Configure::read('appDb.FCS_INCLUDE_STOCK_PRODUCTS_IN_INVOICES')); + if ($isAnonymized) { + $customerResults = $this->Manufacturer->anonymizeCustomersInInvoiceOrOrderList($customerResults); + } $this->setSums($productResults); diff --git a/src/Lib/PdfWriter/MemberCardsPdfWriter.php b/src/Lib/PdfWriter/MemberCardsPdfWriter.php index a25369d7df..8225e12a23 100644 --- a/src/Lib/PdfWriter/MemberCardsPdfWriter.php +++ b/src/Lib/PdfWriter/MemberCardsPdfWriter.php @@ -1,4 +1,6 @@ Manufacturer = FactoryLocator::get('Table')->get('Manufacturers'); } - public function prepareAndSetData($manufacturerId, $pickupDay, $validOrderStates, $orderDetailIds) + public function prepareAndSetData($manufacturerId, $pickupDay, $validOrderStates, $orderDetailIds, $isAnonymized) { $reflect = new \ReflectionClass($this); @@ -64,6 +66,10 @@ public function prepareAndSetData($manufacturerId, $pickupDay, $validOrderStates $orderDetailIds, ); + if ($isAnonymized) { + $results = $this->Manufacturer->anonymizeCustomersInInvoiceOrOrderList($results); + } + $this->setSums($results); $preparedResults = [ diff --git a/src/Lib/PdfWriter/PdfWriter.php b/src/Lib/PdfWriter/PdfWriter.php index d48f629ff1..905f77b9ba 100644 --- a/src/Lib/PdfWriter/PdfWriter.php +++ b/src/Lib/PdfWriter/PdfWriter.php @@ -1,4 +1,6 @@ pdfLibrary->Output('', 'S'); } - /** - * creates folder structure if not yet existings - */ public function writeFile() { $this->setContent(); @@ -96,11 +96,11 @@ public function writeFile() if (file_exists($this->getFilename())) { unlink($this->getFilename()); } - // assure that folder structure exists - $dir = new Folder(); + $path = dirname($this->getFilename()); - $dir->create($path); - $dir->chmod($path, 0755); + if (!file_exists($path)) { + mkdir($path, 0755, true); + } return $this->pdfLibrary->Output($this->getFilename(), 'F'); } diff --git a/src/Lib/PdfWriter/ProductCardsPdfWriter.php b/src/Lib/PdfWriter/ProductCardsPdfWriter.php index 30cfd01d5a..b8e2ff5b64 100644 --- a/src/Lib/PdfWriter/ProductCardsPdfWriter.php +++ b/src/Lib/PdfWriter/ProductCardsPdfWriter.php @@ -1,4 +1,6 @@ read('Auth'); } - $subject = Configure::read('app.cakeServerName') . ' ' . Text::truncate($message, 90) . ' ' . date(Configure::read('DateFormat.DatabaseWithTimeAlt')); + $subject = Configure::read('App.fullBaseUrl') . ' ' . Text::truncate($message, 90) . ' ' . date(Configure::read('DateFormat.DatabaseWithTimeAlt')); try { $email = new Mailer(false); $email->setProfile('debug'); diff --git a/src/Mailer/AppMailer.php b/src/Mailer/AppMailer.php index daa373da19..899881ecd4 100644 --- a/src/Mailer/AppMailer.php +++ b/src/Mailer/AppMailer.php @@ -1,11 +1,13 @@ customerAnonymizationForManufacturers) { + return $outputStringReplacements; + } + + foreach($this->getTo() as $email) { + + $addressManufacturerTable = FactoryLocator::get('Table')->get('AddressManufacturers'); + $addressManufacturer = $addressManufacturerTable->find('all', [ + 'conditions' => [ + 'AddressManufacturers.email' => $email, + 'AddressManufacturers.id_manufacturer > 0', + ], + 'contain' => [ + 'Manufacturers', + ], + ])->first(); + + if (!empty($addressManufacturer) && $addressManufacturer->manufacturer->anonymize_customers) { + $customersTable = FactoryLocator::get('Table')->get('Customers'); + $customersTable->dropManufacturersInNextFind(); + $customers = $customersTable->find('all', [ + 'contain' => [ + 'AddressCustomers', // to make exclude happen using dropManufacturersInNextFind + ], + ]); + foreach($customers as $customer) { + // eg. greeting is ALWAYS firstname - lastname (not respecting app.customerMainNamePart) + $replaceArrays = [ + $customer->firstname . ' ' . $customer->lastname, + $customer->lastname . ' ' . $customer->firstname, + ]; + foreach($replaceArrays as $customerName) { + $outputStringReplacements[$customerName] = Configure::read('app.htmlHelper')->anonymizeCustomerName($customerName, $customer->id_customer); + } + } + } + + } + + return $outputStringReplacements; + } + public function addToQueue(): void { $this->render(); - if (Configure::check('app.outputStringReplacements')) { - $replacedSubject = OutputFilter::replace($this->getOriginalSubject(), Configure::read('app.outputStringReplacements')); + $outputStringReplacements = $this->getAnonymizedCustomersAsStringReplacementArray(); + if (!is_null(Configure::read('app.outputStringReplacements'))) { + $outputStringReplacements = array_merge($outputStringReplacements, Configure::read('app.outputStringReplacements')); + } + + if (!empty($outputStringReplacements)) { + $replacedSubject = OutputFilter::replace($this->getOriginalSubject(), $outputStringReplacements); $this->setSubject($replacedSubject); - $replacedBody = OutputFilter::replace($this->getMessage()->getBodyHtml(), Configure::read('app.outputStringReplacements')); + $replacedBody = OutputFilter::replace($this->getMessage()->getBodyHtml(), $outputStringReplacements); $this->getMessage()->setBodyHtml($replacedBody); } diff --git a/src/Model/Entity/Cronjob.php b/src/Model/Entity/Cronjob.php new file mode 100644 index 0000000000..0b70ef6683 --- /dev/null +++ b/src/Model/Entity/Cronjob.php @@ -0,0 +1,39 @@ + + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ +class Cronjob extends Entity +{ + + protected function _getName($name) + { + return match($name) { + 'BackupDatabase' => __('BackupDatabaseCronjob'), + 'CheckCreditBalance' => __('CheckCreditBalanceCronjob'), + 'EmailOrderReminder' => __('EmailOrderReminderCronjob'), + 'PickupReminder' => __('PickupReminderCronjob'), + 'SendOrderLists' => __('SendOrderListsCronjob'), + 'SendInvoicesToCustomers' => __('SendInvoicesToCustomersCronjob'), + 'SendInvoicesToManufacturers' => __('SendInvoicesToManufacturersCronjob'), + 'SendDeliveryNotes' => __('SendDeliveryNotesCronjob'), + default => $name, + }; + } + +} diff --git a/src/Model/Entity/Customer.php b/src/Model/Entity/Customer.php index 8d5ba7afcb..5f893a1795 100644 --- a/src/Model/Entity/Customer.php +++ b/src/Model/Entity/Customer.php @@ -1,4 +1,6 @@ [ - * 'name' => 'text to show in German language', - * 'access' => [ - * 'manufacturer', // uncertain how that works - * ], - * ], - */ + public $types; public function initialize(array $config): void @@ -239,19 +232,22 @@ private function initTypes() 'order_detail_product_price_changed' => [ 'name' => __('Action_Log_Order_detail_product_price_changed'), 'access' => [ - 'manufacturer' + 'manufacturer', + 'manufacturerAnonymizationEnabled', ] ], 'order_detail_product_quantity_changed' => [ 'name' => __('Action_Log_Order_detail_product_quantity_changed'), 'access' => [ - 'manufacturer' + 'manufacturer', + 'manufacturerAnonymizationEnabled', ] ], 'order_detail_product_amount_changed' => [ 'name' => __('Action_Log_Order_detail_product_amount_changed'), 'access' => [ - 'manufacturer' + 'manufacturer', + 'manufacturerAnonymizationEnabled', ] ], 'order_detail_customer_changed' => [ @@ -260,7 +256,8 @@ private function initTypes() 'order_detail_cancelled' => [ 'name' => __('Action_Log_Ordered_product_cancelled'), 'access' => [ - 'manufacturer' + 'manufacturer', + 'manufacturerAnonymizationEnabled', ] ], 'order_detail_pickup_day_changed' => [ @@ -456,6 +453,9 @@ private function initTypes() ], ], + 'cronjob_changed' => [ + 'name' => __('Action_Log_Cronjob_changed'), + ], 'cronjob_backup_database' => [ 'name' => __('Action_Log_Cronjob_database_backup_done') ], @@ -504,12 +504,24 @@ public function removeCustomerNameFromAllActionLogs($customerName) { $statement = $this->getConnection()->prepare($query); return $statement->execute(); } + public function removeCustomerEmailFromAllActionLogs($email) { $query = 'UPDATE '.$this->getTable().' SET text = REPLACE(text, \'' . $email . '\', \''.Configure::read('app.htmlHelper')->getDeletedCustomerEmail().'\')'; $statement = $this->getConnection()->prepare($query); return $statement->execute(); } + public function getHiddenTypesForManufacturersWithEnabledAnonymization(): array + { + $types = []; + foreach($this->types as $key => $value) { + if (isset($value['access']) && in_array('manufacturerAnonymizationEnabled', $value['access'])) { + $types[] = $key; + } + } + return $types; + } + public function customSave($type, $customerId, $objectId, $objectType, $text, $time=null) { $data2save = [ @@ -529,6 +541,9 @@ public function getTypesForDropdown($appAuth) foreach ($this->types as $type => $value) { if ($appAuth->isManufacturer()) { if (isset($value['access']) && in_array('manufacturer', $value['access'])) { + if ($appAuth->getManufacturerAnonymizeCustomers() && in_array('manufacturerAnonymizationEnabled', $value['access'])) { + continue; + } $result[$type] = $value['name']; } } else { diff --git a/src/Model/Table/AddressCustomersTable.php b/src/Model/Table/AddressCustomersTable.php index 9ab54e885c..5888997625 100644 --- a/src/Model/Table/AddressCustomersTable.php +++ b/src/Model/Table/AddressCustomersTable.php @@ -1,4 +1,5 @@ belongsTo('Manufacturers', [ + 'foreignKey' => 'id_manufacturer' + ]); + } + public function validationDefault(Validator $validator): Validator { $validator->notEmptyString('firstname', __('Please_enter_the_first_name.')); diff --git a/src/Model/Table/AddressesTable.php b/src/Model/Table/AddressesTable.php index 44e05add8f..5beaff32ab 100644 --- a/src/Model/Table/AddressesTable.php +++ b/src/Model/Table/AddressesTable.php @@ -1,4 +1,5 @@ add($field, 'allow-only-one-weekday', [ 'rule' => function ($value, $context) { - if (Configure::read('app.timeHelper')->getDeliveryWeekday() != Configure::read('app.timeHelper')->formatAsWeekday(strtotime($value))) { + if (DeliveryRhythm::getDeliveryWeekday() != Configure::read('app.timeHelper')->formatAsWeekday(strtotime($value))) { return false; } return true; }, 'message' => __('{0}_needs_to_be_a_{1}.', [ $fieldName, - Configure::read('app.timeHelper')->getWeekdayName(Configure::read('app.timeHelper')->getDeliveryWeekday()) + Configure::read('app.timeHelper')->getWeekdayName(DeliveryRhythm::getDeliveryWeekday()) ]) ]); return $validator; diff --git a/src/Model/Table/AttributesTable.php b/src/Model/Table/AttributesTable.php index 2474d56009..763186f443 100644 --- a/src/Model/Table/AttributesTable.php +++ b/src/Model/Table/AttributesTable.php @@ -1,9 +1,9 @@ next_delivery_day = $this->Products->getNextDeliveryDay($product, $appAuth); + $product->next_delivery_day = DeliveryRhythm::getNextDeliveryDayForProduct($product, $appAuth); // stock available check for product $availableQuantity = $product->stock_available->quantity; @@ -242,13 +246,19 @@ public function add($appAuth, $productId, $attributeId, $amount, $orderedQuantit } if (!Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { - if (!$product->manufacturer->active || (!$appAuth->isOrderForDifferentCustomerMode() && !$appAuth->isSelfServiceModeByReferer() && $this->Products->deliveryBreakEnabled($product->manufacturer->no_delivery_days, $product->next_delivery_day))) { - $message = __('The_manufacturer_of_the_product_{0}_has_a_delivery_break_or_product_is_not_activated.', ['' . $product->name . '']); - return [ - 'status' => 0, - 'msg' => $message, - 'productId' => $initialProductId - ]; + if (!$product->manufacturer->active || (!$appAuth->isOrderForDifferentCustomerMode() + && !$appAuth->isSelfServiceModeByReferer() + && $this->Products->deliveryBreakManufacturerEnabled( + $product->manufacturer->no_delivery_days, + $product->next_delivery_day, + $product->manufacturer->stock_management_enabled, + $product->is_stock_product))) { + $message = __('The_manufacturer_of_the_product_{0}_has_a_delivery_break_or_product_is_not_activated.', ['' . $product->name . '']); + return [ + 'status' => 0, + 'msg' => $message, + 'productId' => $initialProductId + ]; } } @@ -266,7 +276,7 @@ public function add($appAuth, $productId, $attributeId, $amount, $orderedQuantit } if (!Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { - if (!$appAuth->isOrderForDifferentCustomerMode() && !$appAuth->isSelfServiceModeByUrl() && !$appAuth->isSelfServiceModeByReferer() && $this->Products->deliveryBreakEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $product->next_delivery_day)) { + if (!$appAuth->isOrderForDifferentCustomerMode() && !$appAuth->isSelfServiceModeByUrl() && !$appAuth->isSelfServiceModeByReferer() && $this->Products->deliveryBreakGlobalEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $product->next_delivery_day)) { $message = __('{0}_has_activated_the_delivery_break_and_product_{1}_cannot_be_ordered.', [ Configure::read('appDb.FCS_APP_NAME'), @@ -290,7 +300,7 @@ public function add($appAuth, $productId, $attributeId, $amount, $orderedQuantit ]; } - if (!$appAuth->isOrderForDifferentCustomerMode() && $product->next_delivery_day == 'delivery-rhythm-triggered-delivery-break') { + if (!$appAuth->isOrderForDifferentCustomerMode() && !$appAuth->isSelfServiceModeByUrl() && !$appAuth->isSelfServiceModeByReferer() && $product->next_delivery_day == 'delivery-rhythm-triggered-delivery-break') { $message = __('{0}_can_be_ordered_next_week.', [ '' . $product->name . '' @@ -344,10 +354,8 @@ public function add($appAuth, $productId, $attributeId, $amount, $orderedQuantit public function setPickupDays($cartProducts, $customerId, $cartType, $appAuth) { $pickupDayTable = FactoryLocator::get('Table')->get('PickupDays'); - $productTable = FactoryLocator::get('Table')->get('Products'); - foreach($cartProducts as &$cartProduct) { - $cartProduct->pickup_day = $productTable->getNextDeliveryDay($cartProduct->product, $appAuth); + $cartProduct->pickup_day = DeliveryRhythm::getNextDeliveryDayForProduct($cartProduct->product, $appAuth); } $pickupDays = []; diff --git a/src/Model/Table/CartsTable.php b/src/Model/Table/CartsTable.php index 88f5bebb04..05a425dd9d 100644 --- a/src/Model/Table/CartsTable.php +++ b/src/Model/Table/CartsTable.php @@ -1,4 +1,5 @@ equals('cancellation_terms_accepted', 1, __('Please_accept_the_information_about_right_of_withdrawal.')); - $validator->equals('general_terms_and_conditions_accepted', 1, __('Please_accept_the_general_terms_and_conditions.')); - $validator->equals('promise_to_pickup_products', 1, __('Please_promise_to_pick_up_the_ordered_products.')); + if (Configure::read('app.rightOfWithdrawalEnabled')) { + $validator->requirePresence('cancellation_terms_accepted', __('Please_accept_the_information_about_right_of_withdrawal')); + $validator->equals('cancellation_terms_accepted', 1, __('Please_accept_the_information_about_right_of_withdrawal.')); + } + if (Configure::read('app.generalTermsAndConditionsEnabled')) { + $validator->requirePresence('general_terms_and_conditions_accepted', 1, __('Please_accept_the_general_terms_and_conditions.')); + $validator->equals('general_terms_and_conditions_accepted', 1, __('Please_accept_the_general_terms_and_conditions.')); + } + if (Configure::read('app.promiseToPickUpProductsCheckboxEnabled')) { + $validator->requirePresence('promise_to_pickup_products', 1, __('Please_promise_to_pick_up_the_ordered_products.')); + $validator->equals('promise_to_pickup_products', 1, __('Please_promise_to_pick_up_the_ordered_products.')); + } + $validator->notEmptyArray('self_service_payment_type', __('Please_select_your_payment_type.')); + return $validator; + } + + /** + * no checkboxes are shown here - do not validate them neither use requirePresence + */ + public function validationSelfServiceForDifferentCustomer(Validator $validator): Validator + { $validator->notEmptyArray('self_service_payment_type', __('Please_select_your_payment_type.')); return $validator; } @@ -74,7 +96,7 @@ public function getAllowOnlyDefinedPickupDaysValidator(Validator $validator, $fi { $validator->add($field, 'allow-only-defined-pickup-days', [ 'rule' => function ($value, $context) { - if (!in_array($value, array_keys(Configure::read('app.timeHelper')->getNextDailyDeliveryDays(14))) + if (!in_array($value, array_keys(DeliveryRhythm::getNextDailyDeliveryDays(21))) || in_array($value, Configure::read('app.htmlHelper')->getGlobalNoDeliveryDaysAsArray())) { return false; } @@ -109,7 +131,8 @@ public function getCart($appAuth, $cartType): array 'id_customer' => $customerId, 'cart_type' => $cartType, ]; - $cart = $this->save($this->newEntity($cart2save)); + $newCartEntity = $this->newEntity($cart2save, ['validate' => false]); + $cart = $this->save($newCartEntity); } $cartProductsTable = FactoryLocator::get('Table')->get('CartProducts'); @@ -167,9 +190,14 @@ public function getCart($appAuth, $cartType): array $productData['productName'] = $cartProduct->product->name; $productData['manufacturerLink'] = $manufacturerLink; - $nextDeliveryDay = $this->Product->getNextDeliveryDay($cartProduct->product, $appAuth); - $nextDeliveryDay = strtotime($nextDeliveryDay); - $productData['nextDeliveryDay'] = Configure::read('app.timeHelper')->getDateFormattedWithWeekday($nextDeliveryDay); + $nextDeliveryDay = DeliveryRhythm::getNextDeliveryDayForProduct($cartProduct->product, $appAuth); + if ($nextDeliveryDay == 'delivery-rhythm-triggered-delivery-break') { + $dateFormattedWithWeekday = __('Delivery_break'); + } else { + $nextDeliveryDay = strtotime($nextDeliveryDay); + $dateFormattedWithWeekday = Configure::read('app.timeHelper')->getDateFormattedWithWeekday($nextDeliveryDay); + } + $productData['nextDeliveryDay'] = $dateFormattedWithWeekday; $preparedCart['CartProducts'][] = $productData; diff --git a/src/Model/Table/CategoriesTable.php b/src/Model/Table/CategoriesTable.php index c6d37faa41..2702827acf 100644 --- a/src/Model/Table/CategoriesTable.php +++ b/src/Model/Table/CategoriesTable.php @@ -1,10 +1,10 @@ setPrimaryKey('id_configuration'); } - /** - * @param string $plugin - * @throws ConfigFileMissingException - * @return string (version) - */ - public function getVersion() + public function getVersion(): string { $versionFileWithPath = ROOT . DS . 'VERSION.txt'; - if (!file_exists($versionFileWithPath)) { throw new ConfigFileMissingException('version file not found: ' . $versionFileWithPath); } - $file = new File($versionFileWithPath); - $version = $file->read(true, 'r'); - + $file = fopen($versionFileWithPath, "r"); + $version = fgets($file); return $version; } diff --git a/src/Model/Table/CronjobLogsTable.php b/src/Model/Table/CronjobLogsTable.php index 2980174a76..def47cf7d9 100644 --- a/src/Model/Table/CronjobLogsTable.php +++ b/src/Model/Table/CronjobLogsTable.php @@ -1,7 +1,10 @@ deleteAll([ + 'DATEDIFF(DATE_FORMAT(FROM_UNIXTIME(' . $timestamp . '), \'%Y-%m-%d\'), created) > ' . $diffInDays, + ]); + + } + } diff --git a/src/Model/Table/CronjobsTable.php b/src/Model/Table/CronjobsTable.php index 13773171b9..5651db4f29 100644 --- a/src/Model/Table/CronjobsTable.php +++ b/src/Model/Table/CronjobsTable.php @@ -1,12 +1,18 @@ inList('time_interval', array_keys($this->getTimeIntervals()), __('The_time_interval_is_not_valid.')); + $validator->allowEmptyString('day_of_month', __('Please_select_a_day_of_month.'), function($context) { + if (!isset($context['data']['time_interval'])) { + return true; + } + if ($context['data']['time_interval'] == 'month') { + return false; + } + return true; + }); + $validator->inList('day_of_month', array_keys($this->getDaysOfMonth()), __('The_day_of_month_is_not_valid.')); + $validator->allowEmptyString('weekday', __('Please_select_a_weekday.'), function($context) { + if (!isset($context['data']['time_interval'])) { + return true; + } + if ($context['data']['time_interval'] == 'week') { + return false; + } + return true; + }); + $validator->inList('weekday', array_keys($this->getWeekdays()), __('The_weekday_is_not_valid.')); + $validator->add('day_of_month', 'time-interval-day-or-week-no-day-of-month', [ + 'rule' => function ($value, $context) { + if (isset($context['data']['time_interval'])) { + if (in_array($context['data']['time_interval'], ['day', 'week'])) { + if ($value == '') { + return true; + } else { + $timeInterval = match($context['data']['time_interval']) { + 'day' => __('daily'), + 'week' => __('weekly'), + }; + return __('No_day_of_month_allowed_for_time_interval_{0}.', [ + $timeInterval, + ]); + } + } + } + return true; + }, + ]); + $validator->add('weekday', 'time-interval-day-or-month-no-weekday', [ + 'rule' => function ($value, $context) { + if (isset($context['data']['time_interval'])) { + if (in_array($context['data']['time_interval'], ['day', 'month'])) { + if ($value == '') { + return true; + } else { + $timeInterval = match($context['data']['time_interval']) { + 'day' => __('daily'), + 'month' => __('monthly'), + }; + return __('No_weekday_allowed_for_time_interval_{0}.', [ + $timeInterval, + ]); + } + } + } + return true; + }, + ]); + $validator->time('not_before_time', __('Please_enter_a_valid_time.')); + return $validator; + } + + public function validationPickupReminder(Validator $validator): Validator + { + $validator = $this->validationDefault($validator); + $validator = $this->getAllowOnlyOneTimeIntervalValidator($validator, 'week', __('weekly')); + return $validator; + } + + public function validationEmailOrderReminder(Validator $validator): Validator + { + $validator = $this->validationDefault($validator); + $validator = $this->getAllowOnlyOneTimeIntervalValidator($validator, 'week', __('weekly')); + return $validator; + } + + public function validationSendDeliveryNotes(Validator $validator): Validator + { + $validator = $this->validationDefault($validator); + $validator = $this->getAllowOnlyOneTimeIntervalValidator($validator, 'month', __('monthly')); + return $validator; + } + + public function validationSendInvoicesToManufacturers(Validator $validator): Validator + { + $validator = $this->validationDefault($validator); + $validator = $this->getAllowOnlyOneTimeIntervalValidator($validator, 'month', __('monthly')); + return $validator; + } + + public function validationSendOrderLists(Validator $validator): Validator + { + $validator = $this->validationDefault($validator); + $validator = $this->getAllowOnlyOneTimeIntervalValidator($validator, 'day', __('daily')); + return $validator; + } + + private function getAllowOnlyOneTimeIntervalValidator($validator, $timeInterval, $timeIntervalString) + { + $validator = $validator->equals('time_interval', $timeInterval, __('The_time_interval_needs_to_equal_"{0}"', [ + $timeIntervalString, + ])); + return $validator; + } + + public function getTimeIntervals() + { + return [ + 'day' => __('daily'), + 'week' => __('weekly'), + 'month' => __('monthly'), + ]; + } + + public function getDaysOfMonth() + { + $days = []; + $i = 1; + while($i<=31) { + $days[$i] = $i; + $i++; + } + $days[0] = __('Last_day_of_month'); + return $days; + } + + public function getWeekdays() + { + $weekdays = [ + 'Monday' => __('Monday'), + 'Tuesday' => __('Tuesday'), + 'Wednesday' => __('Wednesday'), + 'Thursday' => __('Thursday'), + 'Friday' => __('Friday'), + 'Saturday' => __('Saturday'), + 'Sunday' => __('Sunday'), + ]; + return $weekdays; + + } + + public function findAvailable(Query $query, array $options) + { + if (Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS')) { + $query->where(['name <> "SendInvoicesToManufacturers"']); + } else { + $query->where(['name <> "SendInvoicesToCustomers"']); + $query->where(['name <> "SendDeliveryNotes"']); + } + if (!Configure::read('app.htmlHelper')->paymentIsCashless()) { + $query->where(['name <> "CheckCreditBalance"']); + } + if (!Configure::read('app.emailOrderReminderEnabled')) { + $query->where(['name <> "EmailOrderReminder"']); + } + return $query; + } + public function run() { if (empty($this->cronjobRunDay)) { - $this->cronjobRunDay = Configure::read('app.timeHelper')->getTimeObjectUTC(date(Configure::read('DateFormat.DatabaseWithTimeAlt')))->toUnixString(); + $this->cronjobRunDay = (int) Configure::read('app.timeHelper')->getTimeObjectUTC(date(Configure::read('DateFormat.DatabaseWithTimeAlt')))->toUnixString(); } + $this->CronjobLogs->deleteOldLogs($this->cronjobRunDay); + $cronjobs = $this->find('all', [ 'conditions' => [ 'Cronjobs.active' => APP_ON, @@ -108,12 +279,12 @@ public function run() private function executeCronjobAndSaveLog($cronjob, $cronjobRunDayObject) { - $shellName = $cronjob->name . 'Shell'; - if (!file_exists(ROOT . DS . 'src' . DS . 'Shell' . DS . $shellName . '.php')) { - throw new InvalidParameterException('shell not found: ' . $cronjob->name); + $commandName = $cronjob->getOriginalValues()['name'] . 'Command'; + if (!file_exists(ROOT . DS . 'src' . DS . 'Command' . DS . $commandName . '.php')) { + throw new InvalidParameterException('command not found: ' . $commandName); } - $shellClass = '\\App\\Shell\\' . $shellName; - $shell = new $shellClass(); + $commandClass = '\\App\\Command\\' . $commandName; + $command = new $commandClass(); $databasePreparedCronjobRunDay = Configure::read('app.timeHelper')->getTimeObjectUTC( $cronjobRunDayObject->i18nFormat(Configure::read('DateFormat.DatabaseWithTime') @@ -128,20 +299,19 @@ private function executeCronjobAndSaveLog($cronjob, $cronjobRunDayObject) $this->CronjobLogs->save($entity); try { - $success = $shell->main(); - $success = $success !== true ? CronjobLogsTable::FAILURE : CronjobLogsTable::SUCCESS; + $args = new Arguments([], [], []); + $io = new ConsoleIo(); + $success = $command->execute($args, $io); + $success = $success !== $command::CODE_SUCCESS ? CronjobLogsTable::FAILURE : CronjobLogsTable::SUCCESS; } catch (\Exception $e) { - $success = CronjobLogsTable::SUCCESS; - if (get_class($e) != 'Cake\Network\Exception\SocketException') { - $success = CronjobLogsTable::FAILURE; - } + $success = CronjobLogsTable::FAILURE; } $entity->success = $success; $this->CronjobLogs->save($entity); return [ - 'name' => $cronjob->name, + 'name' => $cronjob->getOriginalValues()['name'], 'time_interval' => $cronjob->time_interval, 'created' => $entity->created->i18nFormat(Configure::read('DateFormat.DatabaseWithTime')), 'success' => $success, diff --git a/src/Model/Table/CustomersTable.php b/src/Model/Table/CustomersTable.php index 561a0ee87f..2377a51e34 100644 --- a/src/Model/Table/CustomersTable.php +++ b/src/Model/Table/CustomersTable.php @@ -1,4 +1,5 @@ setTable('customer'); @@ -585,7 +588,9 @@ public function getCreditBalance($customerId) $depositSum = $orderDetailTable->getSumDeposit($customerId); // rounding avoids problems with very tiny numbers (eg. 2.8421709430404E-14) - return round($paymentProductSum - $paybackProductSum + $paymentDepositSum - $productSum - $depositSum, 2); + $creditBalance = round($paymentProductSum - $paybackProductSum + $paymentDepositSum - $productSum - $depositSum, 2); + // "+ 0" converts -0,00 to 0,00 + return $creditBalance + 0; } public function getForDropdown($includeManufacturers = false, $includeOfflineCustomers = true, $conditions = []) diff --git a/src/Model/Table/DepositProductAttributesTable.php b/src/Model/Table/DepositProductAttributesTable.php index 61c31f028f..1391454a48 100644 --- a/src/Model/Table/DepositProductAttributesTable.php +++ b/src/Model/Table/DepositProductAttributesTable.php @@ -1,8 +1,9 @@ toArray(); foreach($invoices as &$invoice) { + foreach($invoice->invoice_taxes as $invoiceTax) { $invoice->total_sum_price_excl += $invoiceTax->total_price_tax_excl; $invoice->total_sum_tax += $invoiceTax->total_price_tax; $invoice->total_sum_price_incl += $invoiceTax->total_price_tax_incl; } + + if (is_null($invoice->total_sum_price_excl)) { + $invoice->total_sum_price_excl = 0; + } + if (is_null($invoice->total_sum_tax)) { + $invoice->total_sum_tax = 0; + } + if (is_null($invoice->total_sum_price_incl)) { + $invoice->total_sum_price_incl = 0; + } + } return $invoices; @@ -114,8 +127,15 @@ public function getPreparedTaxRatesForSumTable($invoices) $taxRatesSums[$trt]['sum_price_excl'] += $invoiceTax->total_price_tax_excl; $taxRatesSums[$trt]['sum_tax'] += $invoiceTax->total_price_tax; $taxRatesSums[$trt]['sum_price_incl'] += $invoiceTax->total_price_tax_incl; - } + + $taxRates[$trt][$taxRate]['sum_price_excl'] = round($taxRates[$trt][$taxRate]['sum_price_excl'], 2); + $taxRates[$trt][$taxRate]['sum_tax'] = round($taxRates[$trt][$taxRate]['sum_tax'], 2); + $taxRates[$trt][$taxRate]['sum_price_incl'] = round($taxRates[$trt][$taxRate]['sum_price_incl'], 2); + $taxRatesSums[$trt]['sum_price_excl'] = round($taxRatesSums[$trt]['sum_price_excl'], 2); + $taxRatesSums[$trt]['sum_tax'] = round($taxRatesSums[$trt]['sum_tax'], 2); + $taxRatesSums[$trt]['sum_price_incl'] = round($taxRatesSums[$trt]['sum_price_incl'], 2); + } } @@ -161,6 +181,12 @@ public function getDataForCustomerInvoice($customerId, $currentDay) $paymentsTable = FactoryLocator::get('Table')->get('Payments'); $deposits = $paymentsTable->getCustomerDepositNotBilled($customerId); + // create empty dummy data for deleted customer + if (is_null($customer)) { + $customer = $customersTable->newEmptyEntity(); + $customer->active_order_details = []; + } + $preparedData = $this->prepareDataForCustomerInvoice($customer->active_order_details, $deposits, null); $customer->active_order_details = $preparedData['active_order_details']; @@ -214,21 +240,22 @@ public function prepareDataForCustomerInvoice($orderDetails, $returnedDeposits, } // prepare delivered deposit + $depositTaxRate = Configure::read('app.numberHelper')->parseFloatRespectingLocale(Configure::read('appDb.FCS_DEPOSIT_TAX_RATE')); $orderDetailTable = FactoryLocator::get('Table')->get('OrderDetails'); $orderedDeposit = $returnedDeposit = ['deposit_incl' => 0, 'deposit_excl' => 0, 'deposit_tax' => 0, 'deposit_amount' => 0, 'entities' => []]; foreach($orderDetails as $orderDetail) { if ($orderDetail->deposit != 0) { $orderedDeposit['deposit_incl'] += $orderDetail->deposit; - $orderedDeposit['deposit_excl'] += $orderDetailTable->getDepositNet($orderDetail->deposit, $orderDetail->product_amount); - $orderedDeposit['deposit_tax'] += $orderDetailTable->getDepositTax($orderDetail->deposit, $orderDetail->product_amount); + $orderedDeposit['deposit_excl'] += $orderDetailTable->getDepositNet($orderDetail->deposit, $orderDetail->product_amount, $depositTaxRate); + $orderedDeposit['deposit_tax'] += $orderDetailTable->getDepositTax($orderDetail->deposit, $orderDetail->product_amount, $depositTaxRate); $orderedDeposit['deposit_amount'] += $orderDetail->product_amount; } } foreach($returnedDeposits as $deposit) { $returnedDeposit['deposit_incl'] += $deposit->amount * -1; - $returnedDeposit['deposit_excl'] += $orderDetailTable->getDepositNet($deposit->amount, 1) * -1; - $returnedDeposit['deposit_tax'] += $orderDetailTable->getDepositTax($deposit->amount, 1) * -1; + $returnedDeposit['deposit_excl'] += $orderDetailTable->getDepositNet($deposit->amount, 1, $depositTaxRate) * -1; + $returnedDeposit['deposit_tax'] += $orderDetailTable->getDepositTax($deposit->amount, 1, $depositTaxRate) * -1; $returnedDeposit['deposit_amount']++; $returnedDeposit['entities'][] = $deposit; } @@ -403,7 +430,7 @@ public function getNextInvoiceNumberForCustomer($currentYear, $lastInvoice) } } - $newIncreasingInvoiceNumber = $this->formatInvoiceNumberWithLeadingZeros($increasingNumberOfLastInvoice, 6); + $newIncreasingInvoiceNumber = $this->formatInvoiceNumberWithLeadingZeros((string) $increasingNumberOfLastInvoice, 6); $newInvoiceNumber = $invoicePrefix . $currentYear . '-' . $newIncreasingInvoiceNumber; return $newInvoiceNumber; @@ -416,14 +443,14 @@ public function getNextInvoiceNumberForManufacturer($invoices) if (! empty($invoices)) { $invoiceNumber = (int) $invoices[0]->invoice_number + 1; } - $newInvoiceNumber = $this->formatInvoiceNumberWithLeadingZeros($invoiceNumber, 4); + $newInvoiceNumber = $this->formatInvoiceNumberWithLeadingZeros((string) $invoiceNumber, 4); return $newInvoiceNumber; } /** * turns eg 24 into 0024 */ - private function formatInvoiceNumberWithLeadingZeros($invoiceNumber, $zeroCount) + private function formatInvoiceNumberWithLeadingZeros(string $invoiceNumber, int $zeroCount): string { return str_pad($invoiceNumber, $zeroCount, '0', STR_PAD_LEFT); } diff --git a/src/Model/Table/ManufacturersTable.php b/src/Model/Table/ManufacturersTable.php index fa34de4af8..51c5c5603f 100644 --- a/src/Model/Table/ManufacturersTable.php +++ b/src/Model/Table/ManufacturersTable.php @@ -1,9 +1,10 @@ Customers->getCustomerName('c') . ' ASC'; - break; - case 'customer': - $orderClause = $this->Customers->getCustomerName('c') . ' ASC, od.product_name ASC'; - break; - } + return array_map(function ($data) { + $data['CustomerName'] = Configure::read('app.htmlHelper')->anonymizeCustomerName($data['CustomerName'], (int) $data['CustomerId']); + return $data; + }, $results); + } + public function getDataForInvoiceOrOrderList($manufacturerId, $order, $dateFrom, $dateTo, $orderState, $includeStockProducts, $orderDetailIds = []) + { + $orderClause = match($order) { + 'product' => 'od.product_name ASC, od.tax_rate ASC, ' . $this->Customers->getCustomerName('c') . ' ASC', + 'customer' => $this->Customers->getCustomerName('c') . ' ASC, od.product_name ASC', + }; $params = [ 'manufacturerId' => $manufacturerId ]; diff --git a/src/Model/Table/OrderDetailFeedbacksTable.php b/src/Model/Table/OrderDetailFeedbacksTable.php index d91e2cc30d..f7e4aa883b 100644 --- a/src/Model/Table/OrderDetailFeedbacksTable.php +++ b/src/Model/Table/OrderDetailFeedbacksTable.php @@ -1,4 +1,5 @@ setTable('order_detail'); @@ -110,7 +117,7 @@ public function getOrderDetailsForDeliveryNotes($manufacturerId, $dateFrom, $dat return $query; } - public function getLastOrderDate($customerId) + public function getLastPickupDay($customerId) { $query = $this->find('all', [ 'conditions' => [ @@ -123,6 +130,89 @@ public function getLastOrderDate($customerId) return $query; } + private function getLastOrFirstOrderYear(string $manufacturerId, string $sort) + { + $conditions = []; + if ($manufacturerId != 'all') { + $conditions['Products.id_manufacturer'] = $manufacturerId; + } + $orderDetail = $this->find('all', [ + 'conditions' => $conditions, + 'order' => [ + 'OrderDetails.pickup_day' => $sort, + ], + 'contain' => [ + 'Products', + ], + ])->first(); + return $orderDetail; + } + + public function getFirstOrderYear(string $manufacturerId = 'all'): int|false + { + $orderDetail = $this->getLastOrFirstOrderYear($manufacturerId, 'ASC'); + if (empty($orderDetail)) { + return false; + } + return (int) $orderDetail->pickup_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Year')); + } + + public function getLastOrderYear(string $manufacturerId = 'all'): int|false + { + $orderDetail = $this->getLastOrFirstOrderYear($manufacturerId, 'DESC'); + if (empty($orderDetail)) { + return false; + } + return (int) $orderDetail->pickup_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Year')); + } + + public function getFirstDayOfLastOrderMonth(string $manufacturerId = 'all'): string|false + { + $orderDetail = $this->getLastOrFirstOrderYear($manufacturerId, 'DESC'); + if (empty($orderDetail)) { + return false; + } + return $orderDetail->pickup_day->i18nFormat('Y-MM') . '-01'; + } + + public function addLastMonthsCondition($query, $firstDayOfLastOrderMonth, $lastMonths) + { + $lastMonths--; + $query->where(function (QueryExpression $exp) use ($firstDayOfLastOrderMonth, $lastMonths) { + return $exp->add('OrderDetails.pickup_day >= DATE_SUB("' . $firstDayOfLastOrderMonth . '", INTERVAL ' . $lastMonths . ' MONTH)'); + }); + return $query; + } + + public function getTotalOrderDetails(string $pickupDay, int $productId, int $attributeId) + { + + if ($pickupDay == 'delivery-rhythm-triggered-delivery-break') { + return null; + } + + $query = $this->find('all', [ + 'conditions' => [ + 'OrderDetails.pickup_day' => $pickupDay, + 'OrderDetails.product_id' => $productId, + 'OrderDetails.product_attribute_id' => $attributeId, + ], + ]); + $query->select([ + 'SumAmount' => $query->func()->sum('OrderDetails.product_amount'), + ]); + $query->group([ + 'OrderDetails.pickup_day', + 'OrderDetails.product_id', + 'OrderDetails.product_attribute_id', + ]); + $query = $query->toArray(); + if (count($query) > 0) { + return $query[0]->SumAmount; + } + return null; + } + public function getOrderDetailsForOrderListPreview($pickupDay) { $query = $this->find('all', [ @@ -227,18 +317,17 @@ public function getTaxSums($orderDetails) } - public function getDepositTax($depositGross, $amount) + public function getDepositTax($depositGross, $amount, $taxRate) { - $vat = 0.2; $depositGrossPerPiece = round($depositGross / $amount, 2); - $depositTax = $depositGrossPerPiece - round($depositGrossPerPiece / (1 + $vat), 2); + $depositTax = $depositGrossPerPiece - round($depositGrossPerPiece / (1 + $taxRate / 100), 2); $depositTax = $depositTax * $amount; return $depositTax; } - public function getDepositNet($depositGross, $amount) + public function getDepositNet($depositGross, $amount, $taxRate) { - $depositNet = $depositGross - $this->getDepositTax($depositGross, $amount); + $depositNet = $depositGross - $this->getDepositTax($depositGross, $amount, $taxRate); return $depositNet; } @@ -251,21 +340,22 @@ public function getLastOrderDetailsForDropdown($customerId) $foundOrders = 0; $result = []; - $i = 0; + $i = 1; while($foundOrders < $ordersToLoad) { - $dateFrom = strtotime('- '.$i * 7 . 'day', strtotime(Configure::read('app.timeHelper')->getOrderPeriodFirstDay(Configure::read('app.timeHelper')->getCurrentDay()))); - $dateTo = strtotime('- '.$i * 7 . 'day', strtotime(Configure::read('app.timeHelper')->getOrderPeriodLastDay(Configure::read('app.timeHelper')->getCurrentDay()))); + $dateFrom = strtotime('- '.$i * 7 . 'day', strtotime(DeliveryRhythm::getOrderPeriodFirstDay(Configure::read('app.timeHelper')->getCurrentDay()))); + $dateTo = strtotime('- '.$i * 7 . 'day', strtotime(DeliveryRhythm::getOrderPeriodLastDay(Configure::read('app.timeHelper')->getCurrentDay()))); - // stop trying to search for valid orders if year is one year ago - if (date('Y', $dateFrom) == date('Y') - 1) { + // stop trying to search for valid orders if year is two years ago + // one year is not enough for usage in first weeks of january + if (date('Y', $dateFrom) == date('Y') - 2) { break; } $orderDetails = $this->getOrderDetailQueryForPeriodAndCustomerId($dateFrom, $dateTo, $customerId); if (count($orderDetails) > 0) { - $deliveryDay = Configure::read('app.timeHelper')->formatToDateShort(date('Y-m-d', Configure::read('app.timeHelper')->getDeliveryDay($dateTo))); + $deliveryDay = Configure::read('app.timeHelper')->formatToDateShort(date('Y-m-d', DeliveryRhythm::getDeliveryDay($dateTo))); $result[$deliveryDay] = __('Pickup_day') . ' ' . $deliveryDay . ' - ' . __('{0,plural,=1{1_product} other{#_products}}', [count($orderDetails)]); $foundOrders++; } @@ -405,17 +495,11 @@ public function getDepositSum($manufacturerId, $groupBy) ]; $sql = 'SELECT SUM(od.deposit) as sumDepositDelivered '; - - switch($groupBy) { - case 'month': - $sql .= ', DATE_FORMAT(od.pickup_day, \'%Y-%c\') as monthAndYear '; - break; - case 'year': - $sql .= ', DATE_FORMAT(od.pickup_day, \'%Y\') as Year '; - break; - default: - break; - } + $sql .= match($groupBy) { + 'month' => ', DATE_FORMAT(od.pickup_day, \'%Y-%c\') as monthAndYear ', + 'year' => ', DATE_FORMAT(od.pickup_day, \'%Y\') as Year ', + default => '', + }; $sql .= 'FROM '.$this->tablePrefix.'order_detail od '; $sql .= 'LEFT JOIN '.$this->tablePrefix.'product p ON p.id_product = od.product_id '; @@ -428,19 +512,11 @@ public function getDepositSum($manufacturerId, $groupBy) $sql .= 'AND DATE_FORMAT(od.pickup_day, \'%Y-%m-%d\') >= :depositForManufacturersStartDate '; - switch($groupBy) { - case 'month': - $sql .= 'GROUP BY monthAndYear '; - $sql .= 'ORDER BY monthAndYear DESC;'; - break; - case 'year': - $sql .= 'GROUP BY Year '; - $sql .= 'ORDER BY Year DESC;'; - break; - default: - $sql .= 'ORDER BY od.pickup_day DESC;'; - break; - } + $sql .= match($groupBy) { + 'month' => 'GROUP BY monthAndYear ORDER BY monthAndYear DESC;', + 'year' => 'GROUP BY Year ORDER BY Year DESC;', + default => 'ORDER BY od.pickup_day DESC;', + }; $statement = $this->getConnection()->prepare($sql); $statement->execute($params); @@ -762,4 +838,5 @@ public function getOrderDetailParams($appAuth, $manufacturerId, $productId, $cus return $odParams; } + } diff --git a/src/Model/Table/PagesTable.php b/src/Model/Table/PagesTable.php index 128f02a115..62eef430c1 100644 --- a/src/Model/Table/PagesTable.php +++ b/src/Model/Table/PagesTable.php @@ -1,4 +1,5 @@ allowEmptyDate('date_add'); $validator->add('date_add', 'allowed-only-today-or-before', [ 'rule' => function ($value, $context) { + if ($value == 0) { + return true; + } $formattedValue = date(Configure::read('DateFormat.DatabaseAlt'), strtotime($value)); - if ($formattedValue >Configure::read('app.timeHelper')->getCurrentDateForDatabase()) { + if ($formattedValue > Configure::read('app.timeHelper')->getCurrentDateForDatabase()) { return false; } return true; diff --git a/src/Model/Table/PickupDaysTable.php b/src/Model/Table/PickupDaysTable.php index 1bb8634fe2..6e4030033c 100644 --- a/src/Model/Table/PickupDaysTable.php +++ b/src/Model/Table/PickupDaysTable.php @@ -1,4 +1,5 @@
'; public const ALLOWED_TAGS_DESCRIPTION = '


'; + private $Catalog; + private $Configuration; + private $Manufacturer; + private $Unit; + public function initialize(array $config): void { $this->setTable('product'); @@ -183,7 +191,7 @@ private function getCorrectDayOfMonthValidator(Validator $validator, $field) $deliveryDayAsWeekdayInEnglish = strtolower(date('l', strtotime($context['data']['delivery_rhythm_first_delivery_day']))); $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), strtotime($context['data']['delivery_rhythm_first_delivery_day'] . ' ' . $ordinal . ' ' . $deliveryDayAsWeekdayInEnglish . ' of this month')); - $deliveryWeekdayName = Configure::read('app.timeHelper')->getWeekdayName(Configure::read('app.timeHelper')->getDeliveryWeekday()); + $deliveryWeekdayName = Configure::read('app.timeHelper')->getWeekdayName(DeliveryRhythm::getDeliveryWeekday()); $message = __('The_first_delivery_day_needs_to_be_a_{0}_{1}_of_the_month.', [ $ordinalForWeekday, $deliveryWeekdayName, @@ -203,106 +211,29 @@ private function getCorrectDayOfMonthValidator(Validator $validator, $field) return $validator; } - public function getNextDeliveryDay($product, $appAuth) + private function deliveryBreakEnabledBase(string|null $noDeliveryDaysAsString, string $deliveryDate): bool { - if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { - $nextDeliveryDay = '1970-01-01'; - } elseif ($appAuth->isOrderForDifferentCustomerMode() || $appAuth->isSelfServiceModeByUrl()) { - $nextDeliveryDay = Configure::read('app.timeHelper')->getCurrentDateForDatabase(); - } else { - $nextDeliveryDay = $this->calculatePickupDayRespectingDeliveryRhythm($product); - } - return $nextDeliveryDay; + return $noDeliveryDaysAsString != '' && preg_match('`' . $deliveryDate . '`', $noDeliveryDaysAsString); } - public function deliveryBreakEnabled($noDeliveryDaysAsString, $deliveryDate) + public function deliveryBreakGlobalEnabled(string|null $noDeliveryDaysAsString, string $deliveryDate): bool { - return $noDeliveryDaysAsString != '' && preg_match('`' . $deliveryDate . '`', $noDeliveryDaysAsString); + return $this->deliveryBreakEnabledBase($noDeliveryDaysAsString, $deliveryDate); } - public function calculatePickupDayRespectingDeliveryRhythm($product, $currentDay=null) + /** + * manufacturer based delivery break is never applied for stock products + */ + public function deliveryBreakManufacturerEnabled( + string|null $noDeliveryDaysAsString, + string $deliveryDate, + bool|int $stockManagementEnabled, + bool|int $isStockProduct): bool { - - if (is_null($currentDay)) { - $currentDay = Configure::read('app.timeHelper')->getCurrentDateForDatabase(); + if ($stockManagementEnabled && $isStockProduct) { + return false; } - - $sendOrderListsWeekday = null; - if (!is_null($product->delivery_rhythm_send_order_list_weekday)) { - $sendOrderListsWeekday = $product->delivery_rhythm_send_order_list_weekday; - } - - $pickupDay = Configure::read('app.timeHelper')->getDbFormattedPickupDayByDbFormattedDate($currentDay, $sendOrderListsWeekday); - - // assure that $product->is_stock_product also contains check for $product->manufacturer->stock_management_enabled - if ($product->is_stock_product) { - return $pickupDay; - } - - if (Configure::read('appDb.FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY')) { - if ($product->delivery_rhythm_type == 'week' && $product->delivery_rhythm_count == 1) { - $regularPickupDay = Configure::read('app.timeHelper')->getDbFormattedPickupDayByDbFormattedDate($currentDay); - if ($pickupDay != $regularPickupDay) { - return 'delivery-rhythm-triggered-delivery-break'; - } - } - } - - if ($product->delivery_rhythm_type == 'week') { - if (!is_null($product->delivery_rhythm_first_delivery_day)) { - $calculatedPickupDay = $product->delivery_rhythm_first_delivery_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); - while($calculatedPickupDay < $pickupDay) { - $calculatedPickupDay = strtotime($calculatedPickupDay . '+' . $product->delivery_rhythm_count . ' week'); - $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), $calculatedPickupDay); - } - - if (Configure::read('appDb.FCS_ALLOW_ORDERS_FOR_DELIVERY_RHYTHM_ONE_OR_TWO_WEEKS_ONLY_IN_WEEK_BEFORE_DELIVERY')) { - if (in_array($product->delivery_rhythm_count, [1, 2]) && $pickupDay != $calculatedPickupDay) { - return 'delivery-rhythm-triggered-delivery-break'; - } - } - - $pickupDay = $calculatedPickupDay; - } - } - - if ($product->delivery_rhythm_type == 'month') { - switch($product->delivery_rhythm_count) { - case '1': - $ordinal = 'first'; - break; - case '2': - $ordinal = 'second'; - break; - case '3': - $ordinal = 'third'; - break; - case '4': - $ordinal = 'fourth'; - break; - case '0': - $ordinal = 'last'; - break; - } - $deliveryDayAsWeekdayInEnglish = strtolower(date('l', strtotime($pickupDay))); - $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), strtotime($currentDay . ' ' . $ordinal . ' ' . $deliveryDayAsWeekdayInEnglish . ' of this month')); - - if (!is_null($product->delivery_rhythm_first_delivery_day)) { - $calculatedPickupDay = $product->delivery_rhythm_first_delivery_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); - } - - while($calculatedPickupDay < $pickupDay) { - $calculatedPickupDay = date(Configure::read('app.timeHelper')->getI18Format('DatabaseAlt'), strtotime($calculatedPickupDay . ' ' . $ordinal . ' ' . $deliveryDayAsWeekdayInEnglish . ' of next month')); - } - $pickupDay = $calculatedPickupDay; - } - - if ($product->delivery_rhythm_type == 'individual') { - $pickupDay = $product->delivery_rhythm_first_delivery_day->i18nFormat(Configure::read('app.timeHelper')->getI18Format('Database')); - } - - return $pickupDay; - + return $this->deliveryBreakEnabledBase($noDeliveryDaysAsString, $deliveryDate); } /** @@ -312,14 +243,13 @@ public function calculatePickupDayRespectingDeliveryRhythm($product, $currentDay */ public function isOwner($productId, $manufacturerId) { - $found = $this->find('all', [ 'conditions' => [ 'Products.id_product' => $productId, 'Products.id_manufacturer' => $manufacturerId ] ])->count(); - return (boolean) $found; + return (bool) $found; } /** @@ -328,7 +258,7 @@ public function isOwner($productId, $manufacturerId) public function getProductIdAndAttributeId($productId): array { $attributeId = 0; - $explodedProductId = explode('-', $productId); + $explodedProductId = explode('-', (string) $productId); if (count($explodedProductId) == 2) { $productId = $explodedProductId[0]; $attributeId = $explodedProductId[1]; @@ -425,7 +355,10 @@ public function changeDeposit($products) foreach ($products as $product) { $productId = key($product); - $deposit = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]); + $deposit = $product[$productId]; + if (is_string($deposit)) { + $deposit = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]); + } if ($deposit < 0) { throw new InvalidParameterException('input format not correct: '.$product[$productId]); } @@ -433,8 +366,13 @@ public function changeDeposit($products) $success = false; foreach ($products as $product) { + $productId = key($product); - $deposit = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]); + + $deposit = $product[$productId]; + if (is_string($deposit)) { + $deposit = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]); + } $ids = $this->getProductIdAndAttributeId($productId); @@ -507,7 +445,10 @@ public function changePrice($products) foreach ($products as $product) { $productId = key($product); - $price = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]['gross_price']); + $price = $product[$productId]['gross_price']; + if (is_string($price)) { + $price = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]['gross_price']); + } if ($price < 0) { throw new InvalidParameterException('input format not correct: '.$product[$productId]['gross_price']); } @@ -517,7 +458,10 @@ public function changePrice($products) foreach ($products as $product) { $productId = key($product); - $price = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]['gross_price']); + $price = $product[$productId]['gross_price']; + if (is_string($price)) { + $price = Configure::read('app.numberHelper')->getStringAsFloat($price); + } $ids = $this->getProductIdAndAttributeId($productId); $productEntity = $this->find('all', [ @@ -552,10 +496,19 @@ public function changePrice($products) $success |= is_object($result); } - if (isset($product[$productId]['unit_product_price_per_unit_enabled'])) { + if (isset($product[$productId]['unit_product_price_per_unit_enabled']) && isset($product[$productId]['unit_product_price_incl_per_unit'])) { + $this->Unit = FactoryLocator::get('Table')->get('Units'); - $priceInclPerUnit = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]['unit_product_price_incl_per_unit']); - $quantityInUnits = Configure::read('app.numberHelper')->getStringAsFloat($product[$productId]['unit_product_quantity_in_units']); + + $priceInclPerUnit = $product[$productId]['unit_product_price_incl_per_unit']; + if (is_string($priceInclPerUnit)) { + $priceInclPerUnit = Configure::read('app.numberHelper')->getStringAsFloat($priceInclPerUnit); + } + $quantityInUnits = $product[$productId]['unit_product_quantity_in_units']; + if (is_string($quantityInUnits)) { + $quantityInUnits = Configure::read('app.numberHelper')->getStringAsFloat($quantityInUnits); + } + $this->Unit->saveUnits( $ids['productId'], $ids['attributeId'], @@ -568,8 +521,8 @@ public function changePrice($products) } } - $success = (boolean) $success; - return $success; + return (bool) $success; + } /** @@ -606,6 +559,10 @@ public function changeQuantity($products) 'id_product' => $ids['productId'] ], ])->first(); + if (is_null($entity)) { + Log::error('entity was empty: productId: ' . $ids['productId'] . ' / attributeId: ' . $ids['attributeId']); + continue; + } $originalPrimaryKey = $this->StockAvailables->getPrimaryKey(); $this->StockAvailables->setPrimaryKey('id_product_attribute'); $this->StockAvailables->save( @@ -854,24 +811,28 @@ public function getProductsForBackend($appAuth, $productIds, $manufacturerId, $a $quantityIsZeroFilterOn = false; $priceIsZeroFilterOn = false; foreach ($conditions as $condition) { - if (!is_array($condition) && preg_match('/'.$this->getIsQuantityMinFilterSetCondition().'/', $condition)) { - $this->getAssociation('ProductAttributes')->setConditions( - [ - 'StockAvailables.quantity < 3' - ] - ); - $quantityIsZeroFilterOn = true; + if (is_int($condition) || !is_array($condition)) { + continue; } - if (!is_array($condition) && preg_match('/'.$this->getIsPriceZeroCondition().'/', $condition)) { - $this->ProductAttributes->setConditions( - [ - 'ProductAttributes.price' => 0 - ] - ); - $priceIsZeroFilterOn = true; + if (is_string($condition)) { + if (preg_match('/'.$this->getIsQuantityMinFilterSetCondition().'/', $condition)) { + $this->getAssociation('ProductAttributes')->setConditions( + [ + 'StockAvailables.quantity < 3' + ] + ); + $quantityIsZeroFilterOn = true; + } + if (preg_match('/'.$this->getIsPriceZeroCondition().'/', $condition)) { + $this->ProductAttributes->setConditions( + [ + 'ProductAttributes.price' => 0 + ] + ); + $priceIsZeroFilterOn = true; + } } } - $contain = [ 'CategoryProducts', 'CategoryProducts.Categories', @@ -1006,7 +967,7 @@ public function getProductsForBackend($appAuth, $productIds, $manufacturerId, $a $imageFile = Configure::read('app.htmlHelper')->removeTimestampFromFile($imageFile); if ($imageFile != '' && !preg_match('/de-default-home/', $imageFile) && file_exists($imageFile)) { $product->image->hash = sha1_file($imageFile); - $product->image->src = Configure::read('app.cakeServerName') . $imageSrc; + $product->image->src = Configure::read('App.fullBaseUrl') . $imageSrc; } } @@ -1196,7 +1157,7 @@ public function getProductsForBackend($appAuth, $productIds, $manufacturerId, $a if (Configure::read('appDb.FCS_SELF_SERVICE_MODE_FOR_STOCK_PRODUCTS_ENABLED')) { $attributeId = $attribute->id_product_attribute ?? 0; - $preparedProduct['system_bar_code'] = $product->system_bar_code . Configure::read('app.numberHelper')->addLeadingZerosToNumber($attributeId, 4); + $preparedProduct['system_bar_code'] = $product->system_bar_code . Configure::read('app.numberHelper')->addLeadingZerosToNumber((string) $attributeId, 4); $preparedProduct['image'] = $product->image; if (!empty($attribute->unit_product_attribute) && $attribute->unit_product_attribute->price_per_unit_enabled) { $preparedProduct['nameForBarcodePdf'] = $product->name . ': ' . $productName; @@ -1500,7 +1461,7 @@ public function add($manufacturer, $productName, $descriptionShort, $description 'id_manufacturer' => $manufacturer->id_manufacturer, 'id_tax' => $this->Manufacturer->getOptionDefaultTaxId($manufacturer->default_tax_id), 'name' => StringComponent::removeSpecialChars(strip_tags(trim($productName))), - 'delivery_rhythm_send_order_list_weekday' => Configure::read('app.timeHelper')->getSendOrderListsWeekday(), + 'delivery_rhythm_send_order_list_weekday' => DeliveryRhythm::getSendOrderListsWeekday(), 'description_short' => StringComponent::prepareWysiwygEditorHtml($descriptionShort, self::ALLOWED_TAGS_DESCRIPTION_SHORT), 'description' => StringComponent::prepareWysiwygEditorHtml($description, self::ALLOWED_TAGS_DESCRIPTION), 'unity' => StringComponent::removeSpecialChars(strip_tags(trim($unity))), diff --git a/src/Model/Table/PurchasePriceProductAttributesTable.php b/src/Model/Table/PurchasePriceProductAttributesTable.php index 5cf2427ea8..7439d87860 100644 --- a/src/Model/Table/PurchasePriceProductAttributesTable.php +++ b/src/Model/Table/PurchasePriceProductAttributesTable.php @@ -1,8 +1,9 @@ __d('admin', 'Purchase_price') . ': ', + 'label' => __('Purchase_price') . ': ', 'oldTaxRate' => $oldPurchasePriceTaxRate, 'newTaxRate' => $taxRate, ]; diff --git a/src/Model/Table/SlidersTable.php b/src/Model/Table/SlidersTable.php index 5630011960..69696afbec 100644 --- a/src/Model/Table/SlidersTable.php +++ b/src/Model/Table/SlidersTable.php @@ -1,4 +1,5 @@ clearProductCache(); + } + public function afterDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options) { - Cache::clearAll(); + $this->clearProductCache(); + } + + public function clearProductCache() + { + $clearCache = true; + + if ($this->getRegistryAlias() == 'OrderDetails') { + $clearCache = false; + if (Configure::read('app.showOrderedProductsTotalAmountInCatalog')) { + $clearCache = true; + } + } + + if ($clearCache) { + Cache::clearAll(); + } + } } \ No newline at end of file diff --git a/src/Network/AppSession.php b/src/Network/AppSession.php index 19223d84e5..15cceb8302 100644 --- a/src/Network/AppSession.php +++ b/src/Network/AppSession.php @@ -1,4 +1,6 @@ Manufacturer = $this->loadModel('Manufacturers'); + $this->Manufacturer = FactoryLocator::get('Table')->get('Manufacturers'); $manufacturer = $this->Manufacturer->getManufacturerByIdForSendingOrderListsOrInvoice($manufacturerId); $validOrderStates = [ @@ -56,7 +59,7 @@ public function run(array $data, $jobId) : void $invoicePeriodMonthAndYear = Configure::read('app.timeHelper')->getLastMonthNameAndYear(); $pdfWriter = new InvoiceToManufacturerPdfWriter(); - $pdfWriter->prepareAndSetData($manufacturer->id_manufacturer, $dateFrom, $dateTo, $invoiceNumber, $validOrderStates, $invoicePeriodMonthAndYear, $invoiceDate); + $pdfWriter->prepareAndSetData($manufacturer->id_manufacturer, $dateFrom, $dateTo, $invoiceNumber, $validOrderStates, $invoicePeriodMonthAndYear, $invoiceDate, $manufacturer->anonymize_customers); $pdfWriter->setFilename($invoicePdfFile); $pdfWriter->writeFile(); @@ -69,7 +72,7 @@ public function run(array $data, $jobId) : void $this->Manufacturer->Invoices->newEntity($invoice2save) ); - $this->OrderDetail = $this->loadModel('OrderDetails'); + $this->OrderDetail = FactoryLocator::get('Table')->get('OrderDetails'); $this->OrderDetail->updateOrderState($dateFrom, $dateTo, $validOrderStates, Configure::read('app.htmlHelper')->getOrderStateBilled(), $manufacturer->id_manufacturer); $sendInvoice = $this->Manufacturer->getOptionSendInvoice($manufacturer->send_invoice); @@ -91,6 +94,8 @@ public function run(array $data, $jobId) : void 'actionLogIdentifier' => 'send-invoice-' . $manufacturer->id_manufacturer, 'actionLogId' => $actionLogId, ]; + + $email->customerAnonymizationForManufacturers = false; // always show contact person in email body $email->addToQueue(); } diff --git a/src/Queue/Task/GenerateOrderListTask.php b/src/Queue/Task/GenerateOrderListTask.php index 9b8fe360de..9383a2343e 100644 --- a/src/Queue/Task/GenerateOrderListTask.php +++ b/src/Queue/Task/GenerateOrderListTask.php @@ -1,11 +1,14 @@ Manufacturer = $this->loadModel('Manufacturers'); - $manufacturer = $this->Manufacturer->getManufacturerByIdForSendingOrderListsOrInvoice($manufacturerId); - - $currentDateForOrderLists = Configure::read('app.timeHelper')->getCurrentDateTimeForFilename(); - - // START generate PDF grouped by PRODUCT $pdfWriter = new OrderListByProductPdfWriter(); $productPdfFile = Configure::read('app.htmlHelper')->getOrderListLink( - $manufacturer->name, $manufacturer->id_manufacturer, $pickupDayDbFormat, __('product'), $currentDateForOrderLists + $manufacturer->name, $manufacturer->id_manufacturer, $pickupDayDbFormat, __('product'), $currentDateForOrderLists, $isAnonymized ); $pdfWriter->setFilename($productPdfFile); - $pdfWriter->prepareAndSetData($manufacturer->id_manufacturer, $pickupDayDbFormat, [], $orderDetailIds); + $pdfWriter->prepareAndSetData($manufacturer->id_manufacturer, $pickupDayDbFormat, [], $orderDetailIds, $isAnonymized); $pdfWriter->writeFile(); - // END generate PDF grouped by PRODUCT + return $productPdfFile; + } - // START generate PDF grouped by CUSTOMER + private function generateOrderListCustomer($isAnonymized, $manufacturer, $pickupDayDbFormat, $currentDateForOrderLists, $orderDetailIds): string + { $pdfWriter = new OrderListByCustomerPdfWriter(); $customerPdfFile = Configure::read('app.htmlHelper')->getOrderListLink( - $manufacturer->name, $manufacturer->id_manufacturer, $pickupDayDbFormat, __('member'), $currentDateForOrderLists + $manufacturer->name, $manufacturer->id_manufacturer, $pickupDayDbFormat, __('member'), $currentDateForOrderLists, $isAnonymized ); $pdfWriter->setFilename($customerPdfFile); - $pdfWriter->prepareAndSetData($manufacturer->id_manufacturer, $pickupDayDbFormat, [], $orderDetailIds); + $pdfWriter->prepareAndSetData($manufacturer->id_manufacturer, $pickupDayDbFormat, [], $orderDetailIds, $isAnonymized); $pdfWriter->writeFile(); - // END generate PDF grouped by CUSTOMER + return $customerPdfFile; + } + + public function run(array $data, $jobId) : void + { + + $pickupDayDbFormat = $data['pickupDayDbFormat']; + $pickupDayFormatted = $data['pickupDayFormatted']; + $manufacturerId = $data['manufacturerId']; + $orderDetailIds = $data['orderDetailIds']; + $actionLogId = $data['actionLogId']; + + $this->Manufacturer = FactoryLocator::get('Table')->get('Manufacturers'); + $manufacturer = $this->Manufacturer->getManufacturerByIdForSendingOrderListsOrInvoice($manufacturerId); + + $currentDateForOrderLists = Configure::read('app.timeHelper')->getCurrentDateTimeForFilename(); + + $attachments = [ + $this->generateOrderListProduct(false, $manufacturer, $pickupDayDbFormat, $currentDateForOrderLists, $orderDetailIds), + $this->generateOrderListCustomer(false, $manufacturer, $pickupDayDbFormat, $currentDateForOrderLists, $orderDetailIds), + ]; + + if ($manufacturer->anonymize_customers) { + $attachments = [ + $this->generateOrderListProduct(true, $manufacturer, $pickupDayDbFormat, $currentDateForOrderLists, $orderDetailIds), + $this->generateOrderListCustomer(true, $manufacturer, $pickupDayDbFormat, $currentDateForOrderLists, $orderDetailIds), + ]; + } $sendEmail = $this->Manufacturer->getOptionSendOrderList($manufacturer->send_order_list); if ($sendEmail) { - $manufacturer = $this->Manufacturer->getManufacturerByIdForSendingOrderListsOrInvoice($manufacturerId); - $ccRecipients = $this->Manufacturer->getOptionSendOrderListCc($manufacturer->send_order_list_cc); $email = new AppMailer(); $email->viewBuilder()->setTemplate('Admin.send_order_list'); $email->setTo($manufacturer->address_manufacturer->email) - ->setAttachments([ - $productPdfFile, - $customerPdfFile, - ]) - ->setSubject(__('Order_lists_for_the_day') . ' ' . $pickupDayFormated) + ->setAttachments($attachments) + ->setSubject(__('Order_lists_for_the_day') . ' ' . $pickupDayFormatted) ->setViewVars([ 'manufacturer' => $manufacturer, 'showManufacturerUnsubscribeLink' => true, @@ -92,16 +103,18 @@ public function run(array $data, $jobId) : void } $email->afterRunParams = [ - 'actionLogIdentifier' => 'send-order-list-' . $manufacturerId . '-' . $pickupDayFormated, + 'actionLogIdentifier' => 'send-order-list-' . $manufacturerId . '-' . $pickupDayFormatted, 'actionLogId' => $actionLogId, 'manufacturerId' => $manufacturerId, 'orderDetailIds' => $orderDetailIds, ]; + + $email->customerAnonymizationForManufacturers = false; // always show contact person in email body $email->addToQueue(); } - $actionLogIdentifier = 'generate-order-list-' . $manufacturerId . '-' . $pickupDayFormated; + $actionLogIdentifier = 'generate-order-list-' . $manufacturerId . '-' . $pickupDayFormatted; $this->updateActionLogSuccess($actionLogId, $actionLogIdentifier, $jobId); } diff --git a/src/Queue/Task/UpdateActionLogTrait.php b/src/Queue/Task/UpdateActionLogTrait.php index 3169265bc1..89233f43ef 100644 --- a/src/Queue/Task/UpdateActionLogTrait.php +++ b/src/Queue/Task/UpdateActionLogTrait.php @@ -1,9 +1,11 @@ ActionLog = $this->loadModel('ActionLogs'); + $this->ActionLog = FactoryLocator::get('Table')->get('ActionLogs'); $search = 'data-identifier="'.$identifier.'"'; $now = new FrozenTime(); @@ -45,7 +48,7 @@ public function updateActionLogFailure($actionLogId, $identifier, $jobId, $error public function updateActionLogSuccess($actionLogId, $identifier, $jobId) { - $this->ActionLog = $this->loadModel('ActionLogs'); + $this->ActionLog = FactoryLocator::get('Table')->get('ActionLogs'); $search = 'not-ok" data-identifier="'.$identifier.'"'; $now = new FrozenTime(); diff --git a/src/Shell/BackupDatabaseShell.php b/src/Shell/BackupDatabaseShell.php deleted file mode 100644 index 23299ae43c..0000000000 --- a/src/Shell/BackupDatabaseShell.php +++ /dev/null @@ -1,102 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -namespace App\Shell; - -use Cake\Mailer\Mailer; -use Cake\Core\Configure; -use Cake\Datasource\ConnectionManager; -use Cake\Filesystem\File; -use Cake\I18n\Number; - -class BackupDatabaseShell extends AppShell -{ - - public function main() - { - parent::main(); - - ini_set('max_execution_time', 300); - ini_set('memory_limit', '256M'); - - $this->ActionLog = $this->getTableLocator()->get('ActionLogs'); - - $this->startTimeLogging(); - - $dbConfig = ConnectionManager::getConfig('default'); - - $backupdir = ROOT . DS . 'files_private' . DS . 'db-backups'; - $filename = 'db-backup-' . date('Y-m-d_H-i-s', time()) . '.sql'; - - if (! is_dir($backupdir)) { - $this->out(' ', 1); - $this->out('Will create "' . $backupdir . '" directory!'); - if (mkdir($backupdir, 0755, true)) { - $this->out('Directory created!'); - } - } - - $configFile = TMP . 'mysql.txt'; - $configFileObject = new File($configFile); - $configFileContent = '[mysqldump] -host=%host% -user=%user% -password="%password%" -'; - $configFileContent = str_replace(['%host%', '%user%', '%password%'], [$dbConfig['host'], $dbConfig['username'], $dbConfig['password']], $configFileContent); - if (isset($dbConfig['port'])) { - $configFileContent .= 'port=' . $dbConfig['port']; - } - - $configFileObject->write($configFileContent); - - $cmdString = Configure::read('app.mysqlDumpCommand'); - $cmdString .= " --defaults-file=" . $configFile . " --allow-keywords --add-drop-table --ignore-table=" . $dbConfig['database'] . ".queued_jobs --complete-insert --no-tablespaces --quote-names " . $dbConfig['database'] . " > " . $backupdir . DS . $filename; - exec($cmdString); - - $configFileObject->delete(); - - // START zip and file sql file - $zip = new \ZipArchive(); - $zipFilename = str_replace('.sql', '.zip', $backupdir . DS . $filename); - $zip->open($zipFilename, \ZipArchive::CREATE); - $zip->addFile($backupdir . DS . $filename, $filename); // 2nd param for no folders in zip file - $zip->close(); - unlink($backupdir . DS . $filename); - // END zip and delete sql file - - $message = __('Database_backup_successful') . ' ('.Number::toReadableSize(filesize($zipFilename)).').'; - - // email zipped file via Mailer (to avoid queue's max 16MB mediumtext limit of AppMailer) - $email = new Mailer(false); - $email->setProfile('debug'); - $email->setTo(Configure::read('app.hostingEmail')) - ->setSubject($message . ': ' . Configure::read('app.cakeServerName')) - ->setAttachments([ - $zipFilename - ]) - ->send(); - - $this->out($message); - - $this->stopTimeLogging(); - - $this->ActionLog->customSave('cronjob_backup_database', 0, 0, '', $message . '
' . $this->getRuntime()); - $this->out($this->getRuntime()); - - return true; - - } -} diff --git a/src/Shell/NpmPostInstallShell.php b/src/Shell/NpmPostInstallShell.php deleted file mode 100644 index e2b5db2bbd..0000000000 --- a/src/Shell/NpmPostInstallShell.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -namespace App\Shell; - -use Cake\Filesystem\File; -use Cake\Filesystem\Folder; - -class NpmPostInstallShell extends AppShell -{ - - public $vendorDir; - /** - * do not call parent::main because db connection might not be available - * @see AppShell::main() - */ - public function main() - { - $this->vendorDir = WWW_ROOT . 'node_modules'; - - $this->fontawesomePath = $this->vendorDir . DS . '@fortawesome' . DS . 'fontawesome-free' . DS; - $this->jqueryBackstretchPath = $this->vendorDir . DS . 'jquery-backstretch' . DS; - $this->jqueryUiPath = $this->vendorDir . DS . 'jquery-ui' . DS; - $this->tooltipsterPath = $this->vendorDir . DS . 'tooltipster' . DS; - - $this->cleanOverheadFromDependencies(); - $this->copyAdaptedElfinderFiles(); - $this->copyJqueryUiImages(); - $this->copyFontawesomeFonts(); - } - - private function cleanOverheadFromDependencies() - { - - $folder = new Folder(); - - $folder->delete($this->jqueryBackstretchPath . DS . 'examples'); - $folder->delete($this->jqueryBackstretchPath . DS . 'test'); - - $folder->delete($this->fontawesomePath . 'js'); - - $file = new File($this->fontawesomePath . 'css' . DS . 'all.min.css'); - $file->delete(); - $file = new File($this->fontawesomePath . 'css' . DS . 'fontawesome.css'); - $file->delete(); - $file = new File($this->fontawesomePath . 'css' . DS . 'fontawesome.min.css'); - $file->delete(); - $file = new File($this->fontawesomePath . 'css' . DS . 'v4-shims.css'); - $file->delete(); - $file = new File($this->fontawesomePath . 'css' . DS . 'v4-shims.min.css'); - $file->delete(); - - $folder->delete($this->jqueryUiPath . 'external'); - - $folder->delete($this->tooltipsterPath . 'demo'); - $folder->delete($this->tooltipsterPath . 'doc'); - - } - - private function copyFontawesomeFonts() - { - $folder = new Folder($this->fontawesomePath . 'webfonts' . DS); - $folder->copy(WWW_ROOT . 'webfonts'); - $this->out('Fontawesome fonts copied.'); - } - - /** - * if asset compress is on (debug=0=) - * images linked in css files have to be located in WEBROOT/cache - */ - private function copyJqueryUiImages() - { - $folder = new Folder($this->jqueryUiPath . 'dist' . DS . 'themes' . DS . 'smoothness' . DS . 'images' . DS); - $folder->copy(WWW_ROOT . 'cache' . DS . 'images'); - $this->out('JQueryUI images copied.'); - } - - private function copyAdaptedElfinderFiles() - { - $elfinderConfigDir = ROOT . DS . 'config' . DS . 'elfinder' . DS; - - $adaptedFiles = [ - $elfinderConfigDir . 'elfinder.html', - $elfinderConfigDir . 'php' . DS . 'connector.minimal.php' - ]; - - foreach ($adaptedFiles as $file) { - copy($file, preg_replace('/config/', 'webroot' . DS . 'js', $file, 1)); - $this->out('Elfinder config file ' . $file . ' copied successfully.'); - } - } -} diff --git a/src/View/AjaxView.php b/src/View/AjaxView.php index 215219ed5f..1f3dfebacb 100644 --- a/src/View/AjaxView.php +++ b/src/View/AjaxView.php @@ -1,4 +1,5 @@ getNextDailyDeliveryDays(365); + $values = DeliveryRhythm::getNextDailyDeliveryDays(365); } else { - $values = Configure::read('app.timeHelper')->getNextWeeklyDeliveryDays(); + $values = DeliveryRhythm::getNextWeeklyDeliveryDays(); } return $values; break; @@ -82,18 +84,6 @@ public function getConfigurationDropdownOption($name, $value, $appAuth) return self::getConfigurationDropdownOptions($name, $appAuth)[$value]; } - public function getConfigurationDropdownEmpty($name) - { - switch($name) { - case 'FCS_MEMBER_FEE_PRODUCTS': - return null; - break; - default: - return null; - break; - } - } - public function getConfigurationMultipleDropdownOptions($name, $value) { switch($name) { diff --git a/src/View/Helper/MenuHelper.php b/src/View/Helper/MenuHelper.php index 67391732c1..94e3ebc76d 100644 --- a/src/View/Helper/MenuHelper.php +++ b/src/View/Helper/MenuHelper.php @@ -1,4 +1,5 @@ getSession()->read('Auth.orderCustomer'); @@ -124,7 +135,7 @@ public function getDeliveryRhythmString($isStockProduct, $deliveryRhythmType, $d } if ($deliveryRhythmType == 'month') { - $deliveryDayAsWeekday = $this->MyTime->getWeekdayName($this->MyTime->getDeliveryWeekday()); + $deliveryDayAsWeekday = $this->MyTime->getWeekdayName(DeliveryRhythm::getDeliveryWeekday()); if ($deliveryRhythmCount > 0) { $deliveryRhythmString = __('every_{0}_{1}_of_a_month', [ $this->MyNumber->ordinal($deliveryRhythmCount), @@ -144,6 +155,19 @@ public function getDeliveryRhythmString($isStockProduct, $deliveryRhythmType, $d return $deliveryRhythmString; } + public function getSendOrderListsWeekdayOptions() + { + $defaultSendOrderListsWeekday = DeliveryRhythm::getSendOrderListsWeekday(); + $weekday3 = $this->MyTime->getNthWeekdayBeforeWeekday(3, $defaultSendOrderListsWeekday); + $weekday2 = $this->MyTime->getNthWeekdayBeforeWeekday(2, $defaultSendOrderListsWeekday); + $weekday1 = $this->MyTime->getNthWeekdayBeforeWeekday(1, $defaultSendOrderListsWeekday); + return [ + $weekday3 => $this->MyTime->getWeekdayName($weekday3) . ' ' . __('midnight'), + $weekday2 => $this->MyTime->getWeekdayName($weekday2) . ' ' . __('midnight'), + $weekday1 => $this->MyTime->getWeekdayName($weekday1) . ' ' . __('midnight') . ' (' . __('default_value') . ')' + ]; + } + public function getDeliveryRhythmTypesForDropdown() { return [ @@ -161,20 +185,12 @@ public function getDeliveryRhythmTypesForDropdown() public function getOrderStateFontawesomeIcon($orderState) { - switch($orderState) - { - case ORDER_STATE_ORDER_PLACED: - return 'fas fa-cart-arrow-down ok'; - break; - case ORDER_STATE_ORDER_LIST_SENT_TO_MANUFACTURER: - return 'far fa-envelope ok'; - break; - case ORDER_STATE_BILLED_CASHLESS: - case ORDER_STATE_BILLED_CASH: - return 'fa fa-lock not-ok'; - break; - } - return ''; + return match($orderState) { + ORDER_STATE_ORDER_PLACED => 'fas fa-cart-arrow-down ok', + ORDER_STATE_ORDER_LIST_SENT_TO_MANUFACTURER => 'far fa-envelope ok', + ORDER_STATE_BILLED_CASHLESS, ORDER_STATE_BILLED_CASH => 'fa fa-lock not-ok', + default => '', + }; } public function wrapJavascriptBlock($content) { @@ -202,17 +218,20 @@ public function getYesNoArray() public function getCurrencyName($currencySymbol) { - switch($currencySymbol) { - case '€': - return 'Euro'; - break; - case '$': - return 'Dollar'; - break; - default: - return ''; - break; - } + return match($currencySymbol) { + '€' => 'Euro', + '$' => 'Dollar', + default => '', + }; + } + + public function getCurrencyIsoCode($currencySymbol) + { + return match($currencySymbol) { + '€' => 'EUR', + '$' => 'USD', + default => '', + }; } public function getFontAwesomeIconForCurrencyName($currencySymbol) @@ -229,11 +248,13 @@ public function getFontAwesomeIconForCurrencyName($currencySymbol) * @param string $page * @return string */ - public function getDocsUrl($page) + public function getDocsUrl($page, $languageCode=null) { - $languageCode = substr(I18n::getLocale(), 0, 2); + if (is_null($languageCode)) { + $languageCode = substr(I18n::getLocale(), 0, 2); + } $url = 'https://foodcoopshop.github.io/' . $languageCode . '/'; - if ($languageCode == 'de') { + if ($languageCode == 'de' || $page == 'settings') { $url .= $page; } return $url; @@ -257,6 +278,17 @@ public function getDeletedCustomerEmail() return __('Deleted_Email_Address'); } + public function anonymizeCustomerName(string $name, int $id): string + { + $words = explode(' ', $name); + $pieces = []; + foreach ($words as $w) { + $pieces[] = mb_substr($w, 0, 1); + } + $anonymizedCustomerName = join('.', $pieces) . '. - ID ' . $id; + return $anonymizedCustomerName; + } + /** * converts eg. months with only one digit with leading zero * @param int $number @@ -359,7 +391,7 @@ public function getCustomerAddress($customer) } /** - * @param array $manufacturer + * @param $manufacturer * @param string $outputType "pdf" of "html" * @return string */ @@ -392,7 +424,7 @@ public function getManufacturerImprint($manufacturer, $outputType, $addressOnly) $imprintLines[] = __('Phone') . ': ' . $manufacturer->address_manufacturer->phone; } } - $imprintLines[] = __('Email') . ': ' . ($outputType == 'html' ? StringComponent::hideEmail($manufacturer->address_manufacturer->email) : $manufacturer->address_manufacturer->email); + $imprintLines[] = __('Email') . ': ' . $manufacturer->address_manufacturer->email; if (!$addressOnly) { if ($manufacturer->homepage != '') { @@ -493,6 +525,27 @@ public function getCartIdFromCartFinishedUrl($url) return (int) $cartId[5]; } + public function getConfigurationTabs() + { + $tabs = []; + $tabs[] = [ + 'name' => ' ' . __('Configurations'), + 'url' => Configure::read('app.slugHelper')->getConfigurationsList(), + 'key' => 'configurations', + ]; + $tabs[] = [ + 'name' => ' ' . __('Cronjobs'), + 'url' => Configure::read('app.slugHelper')->getCronjobsList(), + 'key' => 'cronjobs', + ]; + $tabs[] = [ + 'name' => ' ' . __('Tax_rates'), + 'url' => Configure::read('app.slugHelper')->getTaxesList(), + 'key' => 'tax_rates', + ]; + return $tabs; + } + public function getReportTabs() { $tabs = []; @@ -583,7 +636,7 @@ public function getManufacturerDepositPaymentText($manufacturerDepositPaymentTex */ public function getProductImageIdAsPath($imageId) { - preg_match_all('/[0-9]/', $imageId, $imageIdAsArray); + preg_match_all('/[0-9]/', (string) $imageId, $imageIdAsArray); $imageIdAsPath = implode(DS, $imageIdAsArray[0]); return $imageIdAsPath; } @@ -796,9 +849,13 @@ public function prepareAsUrl($string) return $string; } - public function getOrderListLink($manufacturerName, $manufacturerId, $deliveryDay, $groupTypeLabel, $currentDate) + public function getOrderListLink($manufacturerName, $manufacturerId, $deliveryDay, $groupTypeLabel, $currentDate, $isAnonymized) { - $url = Configure::read('app.folder_order_lists') . DS . date('Y', strtotime($deliveryDay)) . DS . date('m', strtotime($deliveryDay)) . DS; + $url = Configure::read('app.folder_order_lists'); + $url .= DS . date('Y', strtotime($deliveryDay)) . DS . date('m', strtotime($deliveryDay)) . DS; + if ($isAnonymized) { + $url .= 'anonymized' . DS; + } $url .= $deliveryDay . '_' . StringComponent::slugify($manufacturerName) . '_' . $manufacturerId . __('_Order_list_filename_') . $groupTypeLabel . '_' . StringComponent::slugify(Configure::read('appDb.FCS_APP_NAME')) . '-' . $currentDate . '.pdf'; if (Configure::check('app.outputStringReplacements')) { $url = OutputFilter::replace($url, Configure::read('app.outputStringReplacements')); diff --git a/src/View/Helper/MyNumberHelper.php b/src/View/Helper/MyNumberHelper.php index 98a4626c22..eb131c4158 100644 --- a/src/View/Helper/MyNumberHelper.php +++ b/src/View/Helper/MyNumberHelper.php @@ -1,4 +1,5 @@ parseFloatRespectingLocale($float); - + $float = $this->parseFloatRespectingLocale(trim($string)); if ($float === false) { return -1; // do not return false, because 0 is a valid return value! } - return $float; } - public function formatAsCurrency($amount) + public function formatAsCurrency($amount): string { - $amount = round($amount, 2); // 3.325 was rounded to 3.32 without this line + $amount = round((float) $amount, 2); // 3.325 was rounded to 3.32 without this line $currency = self::currency($amount, 'USD'); // e.g. PLN for polish zloty does not return the polish currency symbol $currency = str_replace('$', Configure::read('appDb.FCS_CURRENCY_SYMBOL'), $currency); @@ -61,31 +49,30 @@ public function formatAsCurrency($amount) return $currency; } - public function formatAsUnit($amount, $shortcode) + public function formatAsUnit($amount, $shortcode): string { return self::formatAsDecimal($amount) . ' ' . $shortcode; } - public function formatAsPercent($amount, $decimals = 2) + public function formatAsPercent($amount, $decimals = 2): string { return self::formatAsDecimal($amount, $decimals) . '%'; } /** * shows decimals only if necessary - * @param $rate */ - public function formatTaxRate($rate) + public function formatTaxRate($rate): string { return $rate != intval($rate) ? self::formatAsDecimal($rate, 1) : self::formatAsDecimal($rate, 0); } - public function formatUnitAsDecimal($amount) + public function formatUnitAsDecimal($amount): string { return self::formatAsDecimal($amount, 3, true); } - public function formatAsDecimal($amount, $decimals = 2, $removeTrailingZeros = false) + public function formatAsDecimal($amount, $decimals = 2, $removeTrailingZeros = false): string { $options = [ 'locale' => I18n::getLocale() @@ -101,18 +88,17 @@ public function formatAsDecimal($amount, $decimals = 2, $removeTrailingZeros = f } /** - * self::parseFloat($double, ['locale' => I18n::getLocale()]); did not work with travis! - * @return boolean|mixed + * Number::parseFloat($float, ['locale' => I18n::getLocale()]); did not work with travis! */ - public function parseFloatRespectingLocale($double) + public function parseFloatRespectingLocale($float): bool|float { if (I18n::getLocale() == 'de_DE') { - $double = str_replace(',', '.', $double); // then replace decimal places + $float = str_replace(',', '.', (string) $float); // replace decimal places } - if (!is_numeric($double)) { + if (!is_numeric($float)) { return false; } - return $double; + return (float) $float; } } ?> \ No newline at end of file diff --git a/src/View/Helper/MyTimeHelper.php b/src/View/Helper/MyTimeHelper.php index 5493055740..538ee51876 100644 --- a/src/View/Helper/MyTimeHelper.php +++ b/src/View/Helper/MyTimeHelper.php @@ -1,9 +1,9 @@ __('daily'), + 'week' => __('weekly'), + 'month' => __('monthly'), + }; + } + public function convertSecondsInMinutesAndSeconds($seconds) { $secondsAsInteger = (int) $seconds; @@ -39,7 +48,7 @@ public function convertSecondsInMinutesAndSeconds($seconds) return join(' ', $result); } - public function getAllYearsUntilThisYear($thisYear, $firstYear, $labelPrefix='') + public function getAllYearsUntilThisYear(int $thisYear, int $firstYear, $labelPrefix=''): array { $years = []; while($thisYear >= $firstYear) { @@ -87,29 +96,6 @@ public function getI18Format($formatString) return Configure::read('DateFormat.' . $formatString); } - public function getLastOrderDay($nextDeliveryDay, $deliveryRhythmType, $deliveryRhythmCount, $deliveryRhythmSendOrderListWeekday, $deliveryRhythmOrderPossibleUntil) - { - - if ($nextDeliveryDay == 'delivery-rhythm-triggered-delivery-break') { - return ''; - } - - if ($deliveryRhythmType == 'individual') { - $result = strtotime($deliveryRhythmOrderPossibleUntil->i18nFormat(Configure::read('DateFormat.Database'))); - } else { - $lastOrderWeekday = $this->getNthWeekdayBeforeWeekday(1, $deliveryRhythmSendOrderListWeekday); - $tmpLocale = I18n::getLocale(); - I18n::setLocale('en_US'); - $weekdayAsNameInEnglish = $this->getWeekdayName($lastOrderWeekday); - I18n::setLocale($tmpLocale); - $result = strtotime('last ' . $weekdayAsNameInEnglish, strtotime($nextDeliveryDay)); - } - - $result = date(Configure::read('DateFormat.DatabaseAlt'), $result); - return $result; - - } - public function getLastDayOfGivenMonth($monthAndYear) { return date('t', strtotime($monthAndYear)); @@ -152,50 +138,11 @@ public function getNthWeekdayAfterWeekday($n, $weekday) return $beforeWeekday; } - public function getSendOrderListsWeekday() - { - $sendOrderListsWeekday = Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY') - Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA'); - if ($sendOrderListsWeekday < 0) { - $sendOrderListsWeekday += 7; - } - return $sendOrderListsWeekday; - } - - public function getDeliveryDateByCurrentDayForDb() - { - $deliveryDate = self::getDeliveryDayByCurrentDay(); - $deliveryDate = date($this->getI18Format('DatabaseAlt'), $deliveryDate); - return $deliveryDate; - } - public function getDateFormattedWithWeekday($date) { $date = $this->getWeekdayName($this->formatAsWeekday($date)) . ', ' . date($this->getI18Format('DateShortAlt'), $date); return $date; } - public function getDeliveryDateByCurrentDayFormattedWithWeekday() - { - $deliveryDate = self::getDeliveryDayByCurrentDay(); - return $this->getDateFormattedWithWeekday($deliveryDate); - } - - public function getDeliveryDayByCurrentDay() - { - return self::getDeliveryDay($this->getCurrentDay()); - } - - public function getNextWeeklyDeliveryDays($maxDays=52) - { - $nextDeliveryDay = $this->getDeliveryDateByCurrentDayForDb(); - return $this->getWeekdayFormatedDaysList($nextDeliveryDay, $maxDays, 7); - } - - public function getNextDailyDeliveryDays($maxDays) - { - $nextDeliveryDay = $this->getTomorrowForDatabase(); - return $this->getWeekdayFormatedDaysList($nextDeliveryDay, $maxDays, 1); - } - public function getTomorrowForDatabase() { return $this->getInXDaysForDatabase(1); } @@ -205,18 +152,18 @@ public function getInXDaysForDatabase($days) return date(Configure::read('DateFormat.DatabaseAlt'), strtotime($this->getCurrentDateForDatabase() . ' +' . $days . ' days')); } - private function getWeekdayFormatedDaysList($nextDeliveryDay, $maxDays, $factor) + public function getWeekdayFormattedDaysList($day, $maxDays, $factor) { - $nextDeliveryDays = [ - $nextDeliveryDay => $this->getDateFormattedWithWeekday(strtotime($nextDeliveryDay)) + $days = [ + $day => $this->getDateFormattedWithWeekday(strtotime($day)) ]; $count = 1; while($count < $maxDays) { - $nextCalculatedDeliveryDay = date(Configure::read('DateFormat.DatabaseAlt'), strtotime($nextDeliveryDay . ' + ' . $count * $factor . ' day')); - $nextDeliveryDays[$nextCalculatedDeliveryDay] = $this->getDateFormattedWithWeekday(strtotime($nextCalculatedDeliveryDay)); + $nextCalculatedDay = date(Configure::read('DateFormat.DatabaseAlt'), strtotime($day . ' + ' . $count * $factor . ' day')); + $days[$nextCalculatedDay] = $this->getDateFormattedWithWeekday(strtotime($nextCalculatedDay)); $count++; } - return $nextDeliveryDays; + return $days; } /** @@ -232,25 +179,19 @@ public function getAllCalendarWeeksUntilNow($timestampStart) { $startCalendarWeek = date('W', $timestampStart); - $startYear = date('Y', $timestampStart); - $currentYear = date('Y'); + $startYear = (int) date('Y', $timestampStart); + $currentYear = (int) date('Y'); $allYears = array_reverse($this->getAllYearsUntilThisYear($currentYear, $startYear)); $currentCalendarWeek = date('W'); $result = []; foreach($allYears as $year) { - switch($year) { - case $startYear: - $result = array_merge($result, $this->getCalendarWeeks($startCalendarWeek, $this->getLastCalendarWeekOfYear($startYear), $year)); - break; - case $currentYear: - $result = array_merge($result, $this->getCalendarWeeks(1, $currentCalendarWeek, $year)); - break; - default: - $result = array_merge($result, $this->getCalendarWeeks(1, $this->getLastCalendarWeekOfYear($year), $year)); - break; - } + $result = match($year) { + $startYear => array_merge($result, $this->getCalendarWeeks($startCalendarWeek, $this->getLastCalendarWeekOfYear($startYear), $year)), + $currentYear => array_merge($result, $this->getCalendarWeeks(1, $currentCalendarWeek, $year)), + default => array_merge($result, $this->getCalendarWeeks(1, $this->getLastCalendarWeekOfYear($year), $year)), + }; } return $result; @@ -273,63 +214,6 @@ public function getCalendarWeeks($firstWeek, $lastWeek, $year) return $result; } - public function getDbFormattedPickupDayByDbFormattedDate($date, $sendOrderListsWeekday = null, $deliveryRhythmType = null, $deliveryRhythmCount = null) - { - if (is_null($sendOrderListsWeekday)) { - $sendOrderListsWeekday = $this->getSendOrderListsWeekday(); - } - $pickupDay = $this->getDeliveryDay(strtotime($date), $sendOrderListsWeekday, $deliveryRhythmType, $deliveryRhythmCount); - $pickupDay = date(Configure::read('DateFormat.DatabaseAlt'), $pickupDay); - return $pickupDay; - } - - public function getDeliveryDateForSendOrderListsShell($date) - { - $formattedToday = date(Configure::read('DateFormat.DatabaseAlt'), $date); - $deliveryDay = strtotime($formattedToday . '+' . Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') . ' days'); - $deliveryDay = date($this->getI18Format('DatabaseAlt'), $deliveryDay); - return $deliveryDay; - } - - public function getDeliveryDay($orderDay, $sendOrderListsWeekday = null, $deliveryRhythmType = null, $deliveryRhythmCount = null) - { - if (is_null($deliveryRhythmType)) { - $deliveryRhythmType = 'week'; - } - if (is_null($deliveryRhythmCount)) { - $deliveryRhythmCount = 1; - } - $daysToAddToOrderPeriodLastDay = Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') + 1; - $deliveryDate = strtotime($this->getOrderPeriodLastDay($orderDay) . '+' . $daysToAddToOrderPeriodLastDay . ' days'); - - $weekdayDeliveryDate = $this->formatAsWeekday($deliveryDate); - $weekdayStringDeliveryDate = strtolower(date('l', $deliveryDate)); - - $weekdayOrderDay = $this->formatAsWeekday($orderDay); - $weekdayOrderDay = $weekdayOrderDay % 7; - - if (is_null($sendOrderListsWeekday)) { - $sendOrderListsWeekday = $this->getSendOrderListsWeekday(); - } - - if ($weekdayOrderDay >= $sendOrderListsWeekday && $weekdayOrderDay <= $weekdayDeliveryDate && $deliveryRhythmType != 'individual') { - $preparedOrderDay = date($this->getI18Format('DateShortAlt'), $orderDay); - $deliveryDate = strtotime($preparedOrderDay . '+ ' . $deliveryRhythmCount . ' ' . $deliveryRhythmType . ' ' . $weekdayStringDeliveryDate); - } - - return $deliveryDate; - } - - public function getWeekdaysBetweenOrderSendAndDelivery($delta = 0) - { - $sendOrderListsWeekday = $this->getSendOrderListsWeekday(); - $weekdays = []; - for ($i = $sendOrderListsWeekday; $i <= $sendOrderListsWeekday + Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') + $delta; $i++) { - $weekdays[] = $i; - } - return $weekdays; - } - public function getCurrentWeekday() { return $this->formatAsWeekday($this->getCurrentDay()); @@ -345,97 +229,6 @@ public function getCurrentDay() return time(); } - public function getDeliveryWeekday() - { - return Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY'); - } - - public function getNextDeliveryDay($day) - { - $orderPeriodFirstDay = $this->getOrderPeriodFirstDay($day); - return date($this->getI18Format('DatabaseAlt'), $this->getDeliveryDay(strtotime($orderPeriodFirstDay))); - } - - public function getFormattedNextDeliveryDay($day) - { - return date($this->getI18Format('DateShortAlt'), strtotime($this->getNextDeliveryDay($day))); - } - - /** - * see tests for implementations - * @param $day - * @return $day - */ - public function getOrderPeriodFirstDay($day) - { - - $currentWeekday = $this->formatAsWeekday($day); - $dateDiff = 7 - $this->getSendOrderListsWeekday() + $currentWeekday; - $date = strtotime('-' . $dateDiff . ' day ', $day); - - if ($currentWeekday > $this->getDeliveryWeekday()) { - $date = strtotime('+7 day', $date); - } - - $date = date($this->getI18Format('DateShortAlt'), $date); - - return $date; - } - - /** - * implemented for $this->sendOrderListsWeekday() == monday OR tuesday OR wednesday - * @param $day - * @return $day - */ - public function getOrderPeriodLastDay($day) - { - - $currentWeekday = $this->formatAsWeekday($day); - - if ($currentWeekday == 7) { - $currentWeekday = 0; - } - - if ($currentWeekday == $this->getDeliveryWeekday()) { - $dateDiff = -1 - Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA'); - } - if ($currentWeekday == ($this->getDeliveryWeekday() + 1) % 7) { - $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 5; - } - if ($currentWeekday == ($this->getDeliveryWeekday() + 2) % 7) { - $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 4; - } - if ($currentWeekday == ($this->getDeliveryWeekday() + 3) % 7) { - $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 3; - } - if ($currentWeekday == ($this->getDeliveryWeekday() + 4) % 7) { - $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 2; - } - if ($currentWeekday == ($this->getDeliveryWeekday() + 5) % 7) { - $dateDiff = (Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1) + 1; - } - if ($currentWeekday == ($this->getDeliveryWeekday() + 6) % 7) { - $dateDiff = Configure::read('appDb.FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA') * -1; - } - - $date = date($this->getI18Format('DateShortAlt'), strtotime($dateDiff . ' day ', $day)); - - return $date; - } - - public function getSendOrderListsWeekdayOptions() - { - $defaultSendOrderListsWeekday = $this->getSendOrderListsWeekday(); - $weekday3 = $this->getNthWeekdayBeforeWeekday(3, $defaultSendOrderListsWeekday); - $weekday2 = $this->getNthWeekdayBeforeWeekday(2, $defaultSendOrderListsWeekday); - $weekday1 = $this->getNthWeekdayBeforeWeekday(1, $defaultSendOrderListsWeekday); - return [ - $weekday3 => $this->getWeekdayName($weekday3) . ' ' . __('midnight'), - $weekday2 => $this->getWeekdayName($weekday2) . ' ' . __('midnight'), - $weekday1 => $this->getWeekdayName($weekday1) . ' ' . __('midnight') . ' (' . __('default_value') . ')' - ]; - } - public function getWeekdays() { $weekdays = [ @@ -585,7 +378,7 @@ public function formatToDateShort($dbString) public function formatToDbFormatDate($dateString) { $timestamp = strtotime($dateString); - $result = date(Configure::read('DateFormat.DatabaseAlt'), $timestamp); + $result = date(Configure::read('DateFormat.DatabaseAlt'), (int) $timestamp); return $result; } diff --git a/src/View/Helper/PricePerUnitHelper.php b/src/View/Helper/PricePerUnitHelper.php index 08319af108..5eb12199be 100644 --- a/src/View/Helper/PricePerUnitHelper.php +++ b/src/View/Helper/PricePerUnitHelper.php @@ -1,4 +1,5 @@ &, > => <) // they need to be reconverted for being displayed properly in form inputs if (!is_null($data['value'])) { - $data['value'] = html_entity_decode($data['value']); + $data['value'] = html_entity_decode((string) $data['value']); } return $this->_templates->format('input', [ diff --git a/templates/BlogPosts/detail.php b/templates/BlogPosts/detail.php index b0e054ac1b..07736388f8 100644 --- a/templates/BlogPosts/detail.php +++ b/templates/BlogPosts/detail.php @@ -1,4 +1,6 @@ element('addScript', ['script' => Configure::read('app.jsNamespace').".Helper.init();". Configure::read('app.jsNamespace').".ModalText.init('.cart .input.checkbox label a.open-with-modal');". - Configure::read('app.jsNamespace').".Cart.initCartFinish();". - Configure::read('app.jsNamespace').".Cart.scrollToCartFinishButton();" + Configure::read('app.jsNamespace').".Cart.initCartFinish();" ]); + +if (!empty($appAuth->Cart->getProducts())) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace').".Cart.scrollToCartFinishButton();" + ]); +} + if (!$appAuth->termsOfUseAccepted()) { $this->element('addScript', ['script' => Configure::read('app.jsNamespace') . ".Helper.disableButton($('#CartsDetailForm button.btn-success'));" diff --git a/templates/Carts/order_successful.php b/templates/Carts/order_successful.php index 7be152db8a..8358fc7c51 100644 --- a/templates/Carts/order_successful.php +++ b/templates/Carts/order_successful.php @@ -1,4 +1,6 @@ element('addScript', ['script' => Configure::read('app.jsNamespace').".Helper.init();". Configure::read('app.jsNamespace').".Helper.addPrevAndNextCategoryLinks();". - Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price');". + Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price, .c3 .is-stock-product');". Configure::read('app.jsNamespace').".ModalImage.addLightboxToWysiwygEditorImages('.pw .toggle-content.description img');". Configure::read('app.jsNamespace').".ModalImage.init('.pw a.open-with-modal');". Configure::read('app.jsNamespace').".Helper.bindToggleLinks();". @@ -28,6 +30,13 @@ Configure::read('app.jsNamespace').".Cart.initRemoveFromCartLinks();". Configure::read('app.jsNamespace').".Helper.setFutureOrderDetails('".addslashes(json_encode($appAuth->getFutureOrderDetails()))."');" ]); + +if (Configure::read('app.showOrderedProductsTotalAmountInCatalog')) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace') . ".Helper.initTooltip('.ordered-products-total-amount');" + ]); +} + ?>

Form->control('Customers.email', ['label' => __('Email')]); ?> - + Form->end(); ?> diff --git a/templates/Customers/registration_successful.php b/templates/Customers/registration_successful.php index ca4cc3ac31..8841ab72c7 100644 --- a/templates/Customers/registration_successful.php +++ b/templates/Customers/registration_successful.php @@ -1,4 +1,6 @@ $feedback->privatized_name . $additionalMetaData, ]); } -} - - -?> - +} \ No newline at end of file diff --git a/templates/Localized/render_as_js_file.php b/templates/Localized/render_as_js_file.php index e81a0885da..ab2c2a3fd5 100644 --- a/templates/Localized/render_as_js_file.php +++ b/templates/Localized/render_as_js_file.php @@ -1,4 +1,6 @@ element('addScript', ['script' => Configure::read('app.jsNamespace').".Helper.init();". Configure::read('app.jsNamespace').".Helper.addPrevAndNextManufacturerLinks();". - Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price');". + Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price, .c3 .is-stock-product');". Configure::read('app.jsNamespace').".ModalImage.addLightboxToWysiwygEditorImages('.pw .toggle-content.description img');". Configure::read('app.jsNamespace').".ModalImage.init('.pw a.open-with-modal, .manufacturer-infos a.open-with-modal');". Configure::read('app.jsNamespace').".Helper.initProductAttributesButtons();". @@ -28,6 +30,13 @@ Configure::read('app.jsNamespace').".Cart.initRemoveFromCartLinks();". Configure::read('app.jsNamespace').".Helper.setFutureOrderDetails('".addslashes(json_encode($appAuth->getFutureOrderDetails()))."');" ]); + +if (Configure::read('app.showOrderedProductsTotalAmountInCatalog')) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace') . ".Helper.initTooltip('.ordered-products-total-amount');" + ]); +} + ?>

name; ?> diff --git a/templates/Manufacturers/index.php b/templates/Manufacturers/index.php index 84baaddf5b..af118952df 100644 --- a/templates/Manufacturers/index.php +++ b/templates/Manufacturers/index.php @@ -1,4 +1,6 @@ element('addScript', ['script' => Configure::read('app.jsNamespace').".ModalImage.addLightboxToWysiwygEditorImages('.pw .toggle-content.description img');". Configure::read('app.jsNamespace').".ModalImage.init('.pw a.open-with-modal');". - Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price');". + Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price, .c3 .is-stock-product');". Configure::read('app.jsNamespace').".Helper.bindToggleLinks();". Configure::read('app.jsNamespace').".Helper.initAmountSwitcher();". Configure::read('app.jsNamespace').".Helper.initProductAttributesButtons();". @@ -43,7 +44,13 @@ Configure::read('app.jsNamespace').".Cart.initRemoveFromCartLinks();". Configure::read('app.jsNamespace').".Helper.setFutureOrderDetails('".addslashes(json_encode($appAuth->getFutureOrderDetails()))."');" ]); - + + if (Configure::read('app.showOrderedProductsTotalAmountInCatalog')) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace') . ".Helper.initTooltip('.ordered-products-total-amount');" + ]); + } + $isFirstElement = empty($blogPosts) || $blogPosts->count() == 0; echo '

'; echo __('New_products'); diff --git a/templates/Pages/list_of_allergens.php b/templates/Pages/list_of_allergens.php index b879e3b170..551190c64f 100644 --- a/templates/Pages/list_of_allergens.php +++ b/templates/Pages/list_of_allergens.php @@ -1,4 +1,6 @@ element('addScript', ['script' => Configure::read('app.jsNamespace').".Helper.init();". - Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price');". + Configure::read('app.jsNamespace').".Helper.initTooltip('.ew .price, .c3 .is-stock-product');". Configure::read('app.jsNamespace').".ModalImage.addLightboxToWysiwygEditorImages('.pw .toggle-content.description img');". Configure::read('app.jsNamespace').".ModalImage.init('.pw a.open-with-modal');". Configure::read('app.jsNamespace').".Helper.bindToggleLinks(true);". @@ -27,6 +29,13 @@ Configure::read('app.jsNamespace').".Cart.initRemoveFromCartLinks();". Configure::read('app.jsNamespace').".Helper.setFutureOrderDetails('".addslashes(json_encode($appAuth->getFutureOrderDetails()))."');" ]); + +if (Configure::read('app.showOrderedProductsTotalAmountInCatalog')) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace') . ".Helper.initTooltip('.ordered-products-total-amount');" + ]); +} + ?>

diff --git a/templates/SelfService/index.php b/templates/SelfService/index.php index 436c9bdae8..bf04caa80d 100644 --- a/templates/SelfService/index.php +++ b/templates/SelfService/index.php @@ -1,4 +1,6 @@ isSelfServiceModeByUrl()) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace').".Calculator.init('.quantity-in-units-input-field-wrapper');" + ]); +} + echo $this->element('autoPrintInvoice'); if ($isMobile) { diff --git a/templates/element/acceptUpdatedTermsOfUseForm.php b/templates/element/acceptUpdatedTermsOfUseForm.php index 502fc2a241..c3eb5aa60b 100644 --- a/templates/element/acceptUpdatedTermsOfUseForm.php +++ b/templates/element/acceptUpdatedTermsOfUseForm.php @@ -1,4 +1,6 @@ request->getSession()->read('invoiceRouteForAutoPrint') != '') { $this->element('addScript', ['script' => - Configure::read('app.jsNamespace').".Helper.openPrintDialogForFile('".Configure::read('app.cakeServerName') . $this->request->getSession()->read('invoiceRouteForAutoPrint') . "');" + Configure::read('app.jsNamespace').".Helper.openPrintDialogForFile('".Configure::read('App.fullBaseUrl') . $this->request->getSession()->read('invoiceRouteForAutoPrint') . "');" ]); $this->request->getSession()->delete('invoiceRouteForAutoPrint'); } diff --git a/templates/element/blogPosts.php b/templates/element/blogPosts.php index aa5bfc9895..667cb26800 100644 --- a/templates/element/blogPosts.php +++ b/templates/element/blogPosts.php @@ -1,4 +1,6 @@ user() || $appAuth->isManufacturer()) { - return; -} - $this->element('addScript', ['script' => Configure::read('app.jsNamespace').".Cart.setCartButtonIcon('".$cartButtonIcon."');" ]); +if (!$appAuth->user() || $appAuth->isManufacturer()) { + return; +} + if ($appAuth->Cart->getProducts() !== null) { $this->element('addScript', ['script' => Configure::read('app.jsNamespace').".Cart.initCartProducts('".addslashes(json_encode($appAuth->Cart->getProducts()))."');" diff --git a/templates/element/cart/cancellationTermsCheckbox.php b/templates/element/cart/cancellationTermsCheckbox.php index 5ee6298d72..226e4199f2 100644 --- a/templates/element/cart/cancellationTermsCheckbox.php +++ b/templates/element/cart/cancellationTermsCheckbox.php @@ -1,4 +1,6 @@ '; - $preparedDeliveryDays = $this->Time->getNextDailyDeliveryDays(14); + $preparedDeliveryDays = DeliveryRhythm::getNextDailyDeliveryDays(21); $formattedToDatabaseDeliveryDays = $this->Html->getGlobalNoDeliveryDaysAsArray(); $i = 0; diff --git a/templates/element/cart/variableMemberFeeInfoText.php b/templates/element/cart/variableMemberFeeInfoText.php index 7ef6920509..5c0870c420 100644 --- a/templates/element/cart/variableMemberFeeInfoText.php +++ b/templates/element/cart/variableMemberFeeInfoText.php @@ -1,4 +1,6 @@
+ + next_delivery_day != 'delivery-rhythm-triggered-delivery-break') { + $pickupDayString = $this->Time->getDateFormattedWithWeekday(strtotime($product->next_delivery_day)); + $tooltip = __('{0}_times_ordered_for_pickup_day_{1}.', [ + '' . $orderedTotalAmount . '', + '' . $pickupDayString . '', + ]); + echo '
' . $orderedTotalAmount . '
'; + } + } + ?> + is_stock_product && $product->manufacturer->stock_management_enabled) { + echo ''; + } + $availableQuantity = $stockAvailable->quantity - $stockAvailable->quantity_limit; if ((($product->is_stock_product && $product->manufacturer->stock_management_enabled) || !$stockAvailable->always_available) && $availableQuantity <= Configure::read('appDb.FCS_PRODUCT_AVAILABILITY_LOW')) { ?> - >() + >() \ No newline at end of file diff --git a/templates/element/catalog/cartButton.php b/templates/element/catalog/cartButton.php index fc4495793d..7e42c469db 100644 --- a/templates/element/catalog/cartButton.php +++ b/templates/element/catalog/cartButton.php @@ -1,4 +1,6 @@ is_stock_product && $product->manufacturer->stock_management_enabled) { $availableQuantity = $stockAvailableQuantity - $stockAvailableQuantityLimit; } if (((($product->is_stock_product && $product->manufacturer->stock_management_enabled) || !$stockAvailableAlwaysAvailable) && $availableQuantity <= 0) - || $deliveryBreakEnabled) { + || $deliveryBreakManufacturerEnabled) { - $disabledClass = 'disabled '; + $classes[] = 'disabled'; - if ($deliveryBreakEnabled) { + if ($deliveryBreakManufacturerEnabled) { + $classes[] = 'btn-danger'; $cartButtonIcon = 'fa-times'; $cartButtonLabel = __('Delivery_break') . '!'; } @@ -40,7 +43,7 @@ } ?> - + diff --git a/templates/element/catalog/columns/column1.php b/templates/element/catalog/columns/column1.php index ec41e84dc3..320f3a7ada 100644 --- a/templates/element/catalog/columns/column1.php +++ b/templates/element/catalog/columns/column1.php @@ -1,4 +1,6 @@ '; @@ -25,28 +28,30 @@ echo ''; echo ''; -if ($product->description_short != '') { - echo $product->description_short.'
'; -} +echo '
'; + if ($product->description_short != '') { + echo $product->description_short.'
'; + } -if ($product->description != '') { - echo $this->Html->link( - ' '.__('Show_more'), - 'javascript:void(0);', - [ - 'class' => 'toggle-link', - 'title' => __('More_infos_to_product_{0}', [h($product->name)]), - 'escape' => false - ] - ); - echo '
'.$product->description.'
'; -} + if ($product->description != '') { + echo $this->Html->link( + ' '.__('Show_more'), + 'javascript:void(0);', + [ + 'class' => 'toggle-link', + 'title' => __('More_infos_to_product_{0}', [h($product->name)]), + 'escape' => false + ] + ); + echo '
'.$product->description.'
'; + } +echo '
'; if (!Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { if (!$appAuth->isOrderForDifferentCustomerMode() && !($product->manufacturer->stock_management_enabled && $product->is_stock_product)) { - $lastOrderDay = $this->Time->getLastOrderDay( + $lastOrderDay = DeliveryRhythm::getLastOrderDay( $product->next_delivery_day, $product->delivery_rhythm_type, $product->delivery_rhythm_count, @@ -56,7 +61,7 @@ if (!($product->delivery_rhythm_type == 'week' && $product->delivery_rhythm_count == 1 - && $this->Time->getSendOrderListsWeekday() == $product->delivery_rhythm_send_order_list_weekday + && DeliveryRhythm::getSendOrderListsWeekday() == $product->delivery_rhythm_send_order_list_weekday ) && $lastOrderDay != '' ) { @@ -90,7 +95,7 @@ if (!$appAuth->isSelfServiceModeByUrl() && !$appAuth->isOrderForDifferentCustomerMode()) { if ( $product->next_delivery_day != 'delivery-rhythm-triggered-delivery-break' - && strtotime($product->next_delivery_day) != $this->Time->getDeliveryDayByCurrentDay() + && strtotime($product->next_delivery_day) != DeliveryRhythm::getDeliveryDayByCurrentDay() ) { $weeksAsFloat = (strtotime($product->next_delivery_day) - strtotime(date($this->MyTime->getI18Format('DateShortAlt')))) / 24/60/60; $fullWeeks = (int) ($weeksAsFloat / 7); diff --git a/templates/element/catalog/columns/column3.php b/templates/element/catalog/columns/column3.php index 397a150410..bd9c135863 100644 --- a/templates/element/catalog/columns/column3.php +++ b/templates/element/catalog/columns/column3.php @@ -1,4 +1,6 @@ id_product . '">'; +$classes = [ + 'pw' +]; +$deliveryBreakManufacturerEnabled = $product->delivery_break_enabled ?? false; +if ($deliveryBreakManufacturerEnabled) { + $classes[] = 'delivery-break-enabled'; +} +echo '
'; echo '
'; echo $this->element('catalog/columns/column1', [ diff --git a/templates/element/catalog/productWithAttributes.php b/templates/element/catalog/productWithAttributes.php index c8e5e1266a..d123d1b8ab 100644 --- a/templates/element/catalog/productWithAttributes.php +++ b/templates/element/catalog/productWithAttributes.php @@ -1,4 +1,6 @@ element('catalog/amountWrapper', [ 'product' => $product, 'stockAvailable' => $attribute->stock_available, - 'hideAmountSelector' => $isStockProductOrderPossible + 'orderedTotalAmount' => $attribute->ordered_total_amount ?? null, + 'hideAmountSelector' => $isStockProductOrderPossible, + 'hideIsStockProductIcon' => $appAuth->isSelfServiceModeByUrl(), ]); echo $this->element('catalog/cartButton', [ - 'deliveryBreakEnabled' => $product->delivery_break_enabled ?? false, + 'deliveryBreakManufacturerEnabled' => $product->delivery_break_enabled ?? false, 'productId' => $product->id_product . '-' . $attribute->id_product_attribute, 'product' => $product, 'stockAvailableQuantity' => $attribute->stock_available->quantity, diff --git a/templates/element/catalog/productWithoutAttributes.php b/templates/element/catalog/productWithoutAttributes.php index 53f65b5320..14489ab61e 100644 --- a/templates/element/catalog/productWithoutAttributes.php +++ b/templates/element/catalog/productWithoutAttributes.php @@ -1,4 +1,6 @@ element('catalog/hiddenProductIdField', ['productId' => $product->id_product]); echo $this->element('catalog/amountWrapper', [ 'product' => $product, + 'orderedTotalAmount' => $product->ordered_total_amount ?? null, 'stockAvailable' => $product->stock_available, 'hideAmountSelector' => $isStockProductOrderPossible, + 'hideIsStockProductIcon' => $appAuth->isSelfServiceModeByUrl(), ]); echo $this->element('catalog/cartButton', [ - 'deliveryBreakEnabled' => $product->delivery_break_enabled ?? false, + 'deliveryBreakManufacturerEnabled' => $product->delivery_break_enabled ?? false, 'productId' => $product->id_product, 'product' => $product, 'stockAvailableQuantity' => $product->stock_available->quantity, diff --git a/templates/element/catalog/quantityInUnitsInputFieldForSelfService.php b/templates/element/catalog/quantityInUnitsInputFieldForSelfService.php index d00ea6f395..24f3a124d8 100644 --- a/templates/element/catalog/quantityInUnitsInputFieldForSelfService.php +++ b/templates/element/catalog/quantityInUnitsInputFieldForSelfService.php @@ -1,4 +1,6 @@ : - + + + + +
diff --git a/src/Shell/TestCronjobShell.php b/templates/element/customCssVars.php similarity index 59% rename from src/Shell/TestCronjobShell.php rename to templates/element/customCssVars.php index 0ed5adcae1..585840dbb7 100644 --- a/src/Shell/TestCronjobShell.php +++ b/templates/element/customCssVars.php @@ -1,4 +1,6 @@ - public function main() - { - return true; + \ No newline at end of file diff --git a/templates/element/customThemeStyleSheet.php b/templates/element/customThemeStyleSheet.php deleted file mode 100644 index d23b01b9c8..0000000000 --- a/templates/element/customThemeStyleSheet.php +++ /dev/null @@ -1,162 +0,0 @@ - - * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com - * @link https://www.foodcoopshop.com - */ - -use Cake\Core\Configure; - -?> - - \ No newline at end of file diff --git a/templates/element/email/greeting.php b/templates/element/email/greeting.php index 9b27aaae36..0905839bb0 100644 --- a/templates/element/email/greeting.php +++ b/templates/element/email/greeting.php @@ -1,4 +1,6 @@

:
- Slug->getLogin(); ?>

+ Slug->getLogin(); ?>

: address_customer->email; ?>
:

@@ -28,10 +30,10 @@

:
- Slug->getChangePassword(); ?> + Slug->getChangePassword(); ?>

:
- Slug->getCustomerProfile(); ?> + Slug->getCustomerProfile(); ?>

diff --git a/templates/element/email/tableFoot.php b/templates/element/email/tableFoot.php index 947d415aba..581689a15f 100644 --- a/templates/element/email/tableFoot.php +++ b/templates/element/email/tableFoot.php @@ -1,4 +1,6 @@
+

diff --git a/templates/element/flash/error.php b/templates/element/flash/error.php index 4436f342eb..231b329859 100644 --- a/templates/element/flash/error.php +++ b/templates/element/flash/error.php @@ -1,4 +1,6 @@ +

diff --git a/templates/element/infoBox.php b/templates/element/infoBox.php index 2477589f8d..1cfe0f09de 100644 --- a/templates/element/infoBox.php +++ b/templates/element/infoBox.php @@ -1,4 +1,6 @@ element('localizedJavascript'); echo $this->element('renderJs', ['configs' => ['frontend']]); + + if ($appAuth->isOrderForDifferentCustomerMode()) { + $this->element('addScript', ['script' => + Configure::read('app.jsNamespace').".Helper.initShowLoaderOnContentChange();" + ]); + } + if ($isMobile) { echo '

'; echo $this->Html->script(['/node_modules/slidebars/dist/slidebars.min']); diff --git a/templates/element/layout/header.php b/templates/element/layout/header.php index 6a41db0410..8bed14837e 100644 --- a/templates/element/layout/header.php +++ b/templates/element/layout/header.php @@ -1,4 +1,6 @@ + <?php echo $title_for_layout; ?> - <?php echo Configure::read('appDb.FCS_APP_NAME'); ?> - + element('jsNamespace'); ?> element('renderCss', ['configs' => ['frontend']]); + $renderConfig = 'frontend'; + if ($isMobile) { + $renderConfig = 'frontend_mobile'; + } + echo $this->element('renderCss', ['configs' => [$renderConfig]]); if ($isMobile) { - echo $this->Html->css([ - '/node_modules/slidebars/dist/slidebars', - 'mobile-global', - 'mobile-frontend', - 'mobile-frontend-portrait', - 'mobile-self-service', - 'mobile-frontend-custom', - ]); + echo $this->Html->css(['mobile-frontend-custom']); } if (Configure::read('appDb.FCS_CUSTOMER_CAN_SELECT_PICKUP_DAY')) { echo $this->Html->css(['customer-can-select-pickup-day']); } - echo $this->element('customThemeStyleSheet'); + echo $this->element('customCssVars'); echo $this->Html->css(['custom']); echo $this->element('layout/customHeader'); ?> diff --git a/templates/element/legal/de_DE/directSelling/generalTermsAndConditions.php b/templates/element/legal/de_DE/directSelling/generalTermsAndConditions.php index bcec8e06eb..5ce506fd35 100644 --- a/templates/element/legal/de_DE/directSelling/generalTermsAndConditions.php +++ b/templates/element/legal/de_DE/directSelling/generalTermsAndConditions.php @@ -1,4 +1,6 @@ 1. Geltung -

1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen seiner Internet-Dienstleistung unter der Domain für seine Nutzer erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

+

1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen seiner Internet-Dienstleistung unter der Domain für seine Nutzer erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

1.2. Geschäftsbedingungen des Nutzers kommen nicht zur Anwendung.

2. Leistungen und Entgelte

-

2.1. Die vom Hersteller in der Plattform des Betreibers unter der Domain dargebotenen Waren und Leistungen sind eine unverbindliche Aufforderung des Herstellers an den Vertragspartner, ein verbindliches Anbot für die angebotenen Waren und Leistungen zu legen. Durch die Bestellung im Onlineshop legt der Vertragspartner ein solches verbindliches Anbot. Ein Vertrag zwischen dem Vertragspartner und dem Hersteller kommt erst zustande, wenn der Lieferant mit der Leistungserbringung begonnen hat oder die Waren zur Abholung bereitgelegt hat.

+

2.1. Die vom Hersteller in der Plattform des Betreibers unter der Domain dargebotenen Waren und Leistungen sind eine unverbindliche Aufforderung des Herstellers an den Vertragspartner, ein verbindliches Anbot für die angebotenen Waren und Leistungen zu legen. Durch die Bestellung im Onlineshop legt der Vertragspartner ein solches verbindliches Anbot. Ein Vertrag zwischen dem Vertragspartner und dem Hersteller kommt erst zustande, wenn der Lieferant mit der Leistungserbringung begonnen hat oder die Waren zur Abholung bereitgelegt hat.

2.2. Die auf der Website angegebenen Preise verstehen sich inklusive der gesetzlichen Steuer, jedoch exklusive der Verpackungs- und Versandkosten. Allfällige weitere Kosten (etwa Pfand) sind gesondert ausgewiesen.

@@ -54,7 +56,7 @@

5. Rücktrittsrecht

-

5.1. Informationen über das Rücktrittsrecht erhalten Sie hier.

+

5.1. Informationen über das Rücktrittsrecht erhalten Sie hier.

5.2. Der Lieferant wird von den alternativen Streitbeilegungsstellen "Online-Streitbeilegung" (https://webgate.ec.europa.eu/odr) sowie "Internetombudsmann" (www.ombudsmann.at) erfasst. Wenn der Vertragspartner ein Verbraucher ist, haben diese auf den genannten Plattformen die Möglichkeit, außergerichtliche Streitbeilegung durch eine unparteiische Schlichtungsstelle in Anspruch zu nehmen.

diff --git a/templates/element/legal/de_DE/directSelling/termsOfUse.php b/templates/element/legal/de_DE/directSelling/termsOfUse.php index e71df29c63..d5aaab6603 100644 --- a/templates/element/legal/de_DE/directSelling/termsOfUse.php +++ b/templates/element/legal/de_DE/directSelling/termsOfUse.php @@ -1,4 +1,6 @@ 1. Geltung -

1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für seine Nutzer erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

+

1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für seine Nutzer erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

1.2. Geschäftsbedingungen des Nutzers kommen nicht zur Anwendung.

@@ -45,7 +47,7 @@

4. Rücktrittsrecht

-

4.1. Der Nutzer schließt den Vertrag mit dem jeweiligen Hersteller direkt. Der Nutzer erhält Informationen über das Rücktrittsrecht hier. Grundsätzlich ist das Rücktrittsrecht für die Lieferung von Lebensmittel ausgeschlossen.

+

4.1. Der Nutzer schließt den Vertrag mit dem jeweiligen Hersteller direkt. Der Nutzer erhält Informationen über das Rücktrittsrecht hier. Grundsätzlich ist das Rücktrittsrecht für die Lieferung von Lebensmittel ausgeschlossen.

4.2. Der jeweilige Hersteller wird von den alternativen Streitbeilegungsstellen "Online-Streitbeilegung" (https://webgate.ec.europa.eu/odr) sowie "Internetombudsmann" (www.ombudsmann.at) erfasst. Der Nutzer hat auf den genannten Plattformen die Möglichkeit, außergerichtliche Streitbeilegung durch eine unparteiische Schlichtungsstelle in Anspruch zu nehmen.

diff --git a/templates/element/legal/de_DE/directSelling/termsOfUseForManufacturers.php b/templates/element/legal/de_DE/directSelling/termsOfUseForManufacturers.php index 5d9be25711..750216900e 100644 --- a/templates/element/legal/de_DE/directSelling/termsOfUseForManufacturers.php +++ b/templates/element/legal/de_DE/directSelling/termsOfUseForManufacturers.php @@ -1,4 +1,6 @@ 1. Geltung -

1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für den Hersteller erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

+

1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für den Hersteller erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

1.2. Geschäftsbedingungen des Herstellers kommen nicht zur Anwendung.

diff --git a/templates/element/legal/de_DE/listOfAllergens.php b/templates/element/legal/de_DE/listOfAllergens.php index 933e30588e..b9559d2160 100644 --- a/templates/element/legal/de_DE/listOfAllergens.php +++ b/templates/element/legal/de_DE/listOfAllergens.php @@ -1,4 +1,6 @@ A-1030 Wien
Telefon: +43 1 52 152-0
-E-Mail: dsb@dsb.gv.at
+E-Mail: dsb@dsb.gv.at
Web: https://www.dsb.gv.at

@@ -118,6 +120,11 @@ Dauer: 1 Jahr
Zweck: Speichert, ob die Cookies akzeptiert wurden. +
  • + csrfToken
    + Dauer: So lange, wie die aktuelle Session gültig ist
    + Zweck: Schutz gegen Cross Site Request Forgery +
  • 3.4 Verwaltung von Mitgliedern, Herstellern und Bestellungen

    @@ -164,3 +171,8 @@

    3.4.6 Auskunftsrecht

    Das Mitglied hat jederzeit das Recht auf kostenlose Auskunft.

    + + +

    3.4.7 Datenweitergabe an helloCash

    +

    Die personenbezogenen Daten der Mitglieder werden zum Zweck der Rechnungs- bzw. Belegerstellung an helloCash (www.hellocash.at) übermittelt. Zur Datenschutzerklärung von helloCash.

    + diff --git a/templates/element/legal/de_DE/retail/generalTermsAndConditions.php b/templates/element/legal/de_DE/retail/generalTermsAndConditions.php index 9391eae4fd..c61f695ea6 100644 --- a/templates/element/legal/de_DE/retail/generalTermsAndConditions.php +++ b/templates/element/legal/de_DE/retail/generalTermsAndConditions.php @@ -1,4 +1,6 @@ 4. Rücktrittsrecht -

    4.1. Der Nutzer schließt den Vertrag mit dem Betreiber. Der Nutzer erhält Informationen über das Rücktrittsrecht hier. Grundsätzlich ist das Rücktrittsrecht für die Lieferung von Lebensmittel ausgeschlossen.

    +

    4.1. Der Nutzer schließt den Vertrag mit dem Betreiber. Der Nutzer erhält Informationen über das Rücktrittsrecht hier. Grundsätzlich ist das Rücktrittsrecht für die Lieferung von Lebensmittel ausgeschlossen.

    4.2. Der Betreiber wird von den alternativen Streitbeilegungsstellen "Online-Streitbeilegung" (https://webgate.ec.europa.eu/odr) sowie "Internetombudsmann" (www.ombudsmann.at) erfasst. Der Nutzer hat auf den genannten Plattformen die Möglichkeit, außergerichtliche Streitbeilegung durch eine unparteiische Schlichtungsstelle in Anspruch zu nehmen.

    diff --git a/templates/element/legal/de_DE/retail/termsOfUse.php b/templates/element/legal/de_DE/retail/termsOfUse.php index abbdbef573..44d7518a6a 100644 --- a/templates/element/legal/de_DE/retail/termsOfUse.php +++ b/templates/element/legal/de_DE/retail/termsOfUse.php @@ -1,4 +1,6 @@ 1. Geltung -

    1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für seine Nutzer erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

    +

    1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für seine Nutzer erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

    1.2. Geschäftsbedingungen des Nutzers kommen nicht zur Anwendung.

    @@ -49,7 +51,7 @@

    4. Rücktrittsrecht

    -

    4.1. Der Nutzer schließt den Vertrag mit dem Betreiber. Der Nutzer erhält Informationen über das Rücktrittsrecht hier. Grundsätzlich ist das Rücktrittsrecht für die Lieferung von Lebensmittel ausgeschlossen.

    +

    4.1. Der Nutzer schließt den Vertrag mit dem Betreiber. Der Nutzer erhält Informationen über das Rücktrittsrecht hier. Grundsätzlich ist das Rücktrittsrecht für die Lieferung von Lebensmittel ausgeschlossen.

    4.2. Der Betreiber wird von den alternativen Streitbeilegungsstellen "Online-Streitbeilegung" (https://webgate.ec.europa.eu/odr) sowie "Internetombudsmann" (www.ombudsmann.at) erfasst. Der Nutzer hat auf den genannten Plattformen die Möglichkeit, außergerichtliche Streitbeilegung durch eine unparteiische Schlichtungsstelle in Anspruch zu nehmen.

    diff --git a/templates/element/legal/de_DE/retail/termsOfUseForManufacturers.php b/templates/element/legal/de_DE/retail/termsOfUseForManufacturers.php index 19ff7632a4..217f9ba243 100644 --- a/templates/element/legal/de_DE/retail/termsOfUseForManufacturers.php +++ b/templates/element/legal/de_DE/retail/termsOfUseForManufacturers.php @@ -1,4 +1,6 @@ 1. Geltung -

    1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für den Hersteller erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

    +

    1.1. Für alle gegenwärtigen und zukünftigen Leistungen, die der Betreiber im Rahmen ihrer Internet-Dienstleistung unter der Domain für den Hersteller erbringt (im Folgenden gemeinsam kurz: die Leistung), gelten ausschließlich die nachfolgenden Bedingungen.

    1.2. Geschäftsbedingungen des Herstellers kommen nicht zur Anwendung.

    diff --git a/templates/element/legal/de_DE/rightOfWithdrawalTerms.php b/templates/element/legal/de_DE/rightOfWithdrawalTerms.php index 042da7c889..73949e0107 100644 --- a/templates/element/legal/de_DE/rightOfWithdrawalTerms.php +++ b/templates/element/legal/de_DE/rightOfWithdrawalTerms.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + if (!$this->Html->paymentIsCashless() || !$appAuth->user() || $appAuth->isSelfServiceCustomer()) { return; } diff --git a/templates/element/selfService/paymentType.php b/templates/element/selfService/paymentType.php index afc15c3264..1cf496fe3a 100644 --- a/templates/element/selfService/paymentType.php +++ b/templates/element/selfService/paymentType.php @@ -1,4 +1,6 @@ getManufacturerName(); } +$this->element('addScript', [ + 'script' => Configure::read('app.jsNamespace') . ".ColorMode.initToggle();" +]); +$menu[] = ['slug' => 'javascript:void(0)', 'name' => '', 'options' => ['fa-icon' => 'ok fa-fw far fa-moon', 'class' => ['color-mode-toggle']]]; if ($appAuth->user()) { if (!$appAuth->isOrderForDifferentCustomerMode()) { $menu[] = ['slug' => $profileSlug, 'name' => $userName, 'options' => ['fa-icon' => 'ok fa-fw fa-user']]; @@ -42,11 +48,11 @@ } if ($appAuth->user() && !$appAuth->isCustomer() && !$appAuth->isOrderForDifferentCustomerMode()) { - $menu[0]['children'][] = ['slug' => $this->Slug->getAdminHome(), 'name' => $adminName, 'options' => ['fa-icon' => 'ok fa-fw fa-gear']]; + $menu[1]['children'][] = ['slug' => $this->Slug->getAdminHome(), 'name' => $adminName, 'options' => ['fa-icon' => 'ok fa-fw fa-gear']]; } if ($appAuth->isCustomer()) { - $menu[0]['children'] = $this->Menu->getCustomerMenuElements($appAuth); + $menu[1]['children'] = $this->Menu->getCustomerMenuElements($appAuth); } if (!$appAuth->isOrderForDifferentCustomerMode()) { @@ -65,13 +71,13 @@ $authMenuElement = $this->Menu->getAuthMenuElement($appAuth); if ($appAuth->user()) { if (!is_null($selfServiceMenuElement)) { - $menu[0]['children'][] = $selfServiceMenuElement; + $menu[1]['children'][] = $selfServiceMenuElement; } - $menu[0]['children'][] = $authMenuElement; + $menu[1]['children'][] = $authMenuElement; } else { $menu[] = $authMenuElement; if (!is_null($selfServiceMenuElement)) { - $menu[0]['children'][] = $selfServiceMenuElement; + $menu[1]['children'][] = $selfServiceMenuElement; } } diff --git a/templates/email/html/customer_activated.php b/templates/email/html/customer_activated.php index b86acdccdd..ac60cda27c 100644 --- a/templates/email/html/customer_activated.php +++ b/templates/email/html/customer_activated.php @@ -1,4 +1,6 @@ :
    - Slug->getActivateEmailAddress($data->activate_email_code); ?> + Slug->getActivateEmailAddress($data->activate_email_code); ?>

    +

    + getUsername(), + $formattedPickupDay, + ]) . ':'; + ?> +

    +

    + ' . $comment . ''; + ?> +

    +
    '; - echo __('You_can_subscribe_our_newsletter_in_the_admin_areas_menu_point_my_data.', [Configure::read('app.cakeServerName') . $this->Slug->getCustomerProfile()]); + echo __('You_can_subscribe_our_newsletter_in_the_admin_areas_menu_point_my_data.', [Configure::read('App.fullBaseUrl') . $this->Slug->getCustomerProfile()]); echo '
    '; - echo __('You_can_subscribe_our_newsletter_in_the_admin_areas_menu_point_my_data.', [Configure::read('app.cakeServerName') . $this->Slug->getCustomerProfile()]); + echo __('You_can_subscribe_our_newsletter_in_the_admin_areas_menu_point_my_data.', [Configure::read('App.fullBaseUrl') . $this->Slug->getCustomerProfile()]); echo '
    - - + +
    - in_your_settings.', [Configure::read('app.cakeServerName') . $this->Slug->getManufacturerMyOptions()]); ?> + in_your_settings.', [Configure::read('App.fullBaseUrl') . $this->Slug->getManufacturerMyOptions()]); ?>

    newsletter_enabled) && !$newsletterCustomer->newsletter_enabled) { - echo __('You_can_subscribe_our_newsletter_in_the_admin_areas_menu_point_my_data.', [Configure::read('app.cakeServerName') . $this->Slug->getCustomerProfile()]); + echo __('You_can_subscribe_our_newsletter_in_the_admin_areas_menu_point_my_data.', [Configure::read('App.fullBaseUrl') . $this->Slug->getCustomerProfile()]); echo '

    '; } ?> @@ -60,7 +62,7 @@ echo Configure::read('appDb.FCS_APP_NAME').'
    '; echo Configure::read('appDb.FCS_APP_ADDRESS').'
    '; echo ''.Configure::read('appDb.FCS_APP_EMAIL').'
    '; - echo ''.preg_replace('/http(s)?\:\/\//', '', Configure::read('app.cakeServerName')).''; + echo ''.$this->MyHtml->getHostWithoutProtocol(Configure::read('App.fullBaseUrl')).''; ?> user()) { ?>

    : diff --git a/templates/layout/error.php b/templates/layout/error.php index 9675d2acf3..51db201bef 100644 --- a/templates/layout/error.php +++ b/templates/layout/error.php @@ -1,4 +1,6 @@ element('jsNamespace'); ?> element('customCssVars'); // do not use AssetCompressPlugin here (not loaded for errors!) echo $this->Html->css([ 'reset', '/node_modules/bootstrap/dist/css/bootstrap.css', + 'dark-mode', 'global', 'fonts', 'frontend', @@ -40,9 +44,6 @@ if ($isMobile) { echo $this->Html->css(['mobile-error']); } - - echo $this->element('customThemeStyleSheet'); - ?> diff --git a/templates/layout/gif/default.php b/templates/layout/gif/default.php index 239b5e4f16..884daff106 100644 --- a/templates/layout/gif/default.php +++ b/templates/layout/gif/default.php @@ -1,4 +1,6 @@ dbConnection = ConnectionManager::get('test'); + $this->seedTestDatabase(); + $this->resetLogs(); + $this->Configuration = $this->getTableLocator()->get('Configurations'); + $this->Configuration->loadConfigurations(); + $View = new View(); $this->Slug = new SlugHelper($View); $this->Html = new MyHtmlHelper($View); $this->Time = new MyTimeHelper($View); $this->Network = new NetworkHelper($View); $this->PricePerUnit = new PricePerUnitHelper($View); - $this->Configuration = $this->getTableLocator()->get('Configurations'); $this->Customer = $this->getTableLocator()->get('Customers'); $this->Manufacturer = $this->getTableLocator()->get('Manufacturers'); - $this->resetTestDatabaseData(); - $this->resetLogs(); - $this->Configuration->loadConfigurations(); - - // enable security token only for IntegrationTests + // enable tokens only for IntegrationTests if (method_exists($this, 'enableSecurityToken')) { $this->enableSecurityToken(); + $this->enableCsrfToken(); } $this->useCommandRunner(); @@ -86,17 +89,17 @@ public function setUp(): void TestEmailTransport::clearMessages(); } - private function getLogFile($name) + private function getLogFile(string $name): string { - return new File(ROOT . DS . 'logs' . DS . $name . '.log'); + return ROOT . DS . 'logs' . DS . $name . '.log'; } - protected function resetLogs() + protected function resetLogs(): void { - $this->getLogFile('debug')->write(''); - $this->getLogFile('error')->write(''); - $this->getLogFile('cli-debug')->write(''); - $this->getLogFile('cli-error')->write(''); + file_put_contents($this->getLogFile('debug'), ''); + file_put_contents($this->getLogFile('error'), ''); + file_put_contents($this->getLogFile('cli-debug'), ''); + file_put_contents($this->getLogFile('cli-error'), ''); } public function tearDown(): void @@ -107,18 +110,20 @@ public function tearDown(): void protected function assertLogFilesForErrors() { - $log = $this->getLogFile('debug')->read(true, 'r'); - $log .= $this->getLogFile('error')->read(true, 'r'); - $log .= $this->getLogFile('cli-debug')->read(true, 'r'); - $log .= $this->getLogFile('cli-error')->read(true, 'r'); + $log = file_get_contents($this->getLogFile('debug')); + $log .= file_get_contents($this->getLogFile('error')); + $log .= file_get_contents($this->getLogFile('cli-debug')); + $log .= file_get_contents($this->getLogFile('cli-error')); $this->assertDoesNotMatchRegularExpression('/(Warning|Notice)/', $log); } - protected function resetTestDatabaseData() + protected function seedTestDatabase() { - $this->dbConnection = ConnectionManager::get('test'); - $this->testDumpDir = TESTS . 'config' . DS . 'sql' . DS; - $this->dbConnection->query(file_get_contents($this->testDumpDir . 'test-db-data.sql')); + $migrations = new Migrations(); + $migrations->seed([ + 'connection' => 'test', + 'source' => 'Seeds' . DS . 'tests', // needs to be a subfolder of config + ]); } protected function getJsonDecodedContent() @@ -138,7 +143,7 @@ protected function assertAccessDeniedFlashMessage() { protected function assertRedirectToLoginPage() { - $this->assertRegExpWithUnquotedString('http://localhost' . $this->Slug->getLogin(), $this->_response->getHeaderLine('Location')); + $this->assertRegExpWithUnquotedString(Configure::read('App.fullBaseUrl') . $this->Slug->getLogin(), $this->_response->getHeaderLine('Location')); } protected function assertJsonOk() @@ -152,7 +157,7 @@ protected function assertJsonOk() */ protected function assertNotPerfectlyImplementedAccessRestricted() { - $this->assertEquals('http://localhost/', $this->_response->getHeaderLine('Location')); + $this->assertEquals(Configure::read('App.fullBaseUrl') . '/' , $this->_response->getHeaderLine('Location')); } /** @@ -163,6 +168,7 @@ protected function assertNotPerfectlyImplementedAccessRestricted() */ protected function assertRegExpWithUnquotedString($unquotedString, $response, $msg = '') { + if (is_null($response)) return; $this->assertMatchesRegularExpression('`' . preg_quote($unquotedString) . '`', $response, $msg); } @@ -182,33 +188,16 @@ protected function assertUrl($url, $expectedUrl, $msg = '') $this->assertEquals($url, $expectedUrl, $msg); } - protected function changeReadOnlyConfiguration($configKey, $value) - { - $query = 'UPDATE ' . $this->Configuration->getTable() . ' SET value = :value WHERE name = :configKey'; - $params = [ - 'value' => $value, - 'configKey' => $configKey - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); - $this->Configuration->loadConfigurations(); - } - /** - * needs to login as superadmin and logs user out automatically - * - * @param string $configKey - * @param string $newValue + * automatically logout of user */ - protected function changeConfiguration($configKey, $newValue) + protected function changeConfiguration(string $configKey, $value) { - $query = 'UPDATE fcs_configuration SET value = :newValue WHERE name = :configKey;'; - $params = [ - 'newValue' => $newValue, - 'configKey' => $configKey - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $this->Configuration->setPrimaryKey('name'); + $configurationEntity = $this->Configuration->get($configKey); + $configurationEntity->value = $value; + $this->Configuration->save($configurationEntity); + $this->Configuration->setPrimaryKey('id_configuration'); $this->Configuration->loadConfigurations(); $this->logout(); } @@ -219,21 +208,14 @@ protected function debug($content) ob_flush(); } - protected function changeManufacturerNoDeliveryDays($manufacturerId, $noDeliveryDays = '') + protected function changeManufacturerNoDeliveryDays(int $manufacturerId, string $noDeliveryDays = ''): void { - $query = 'UPDATE fcs_manufacturer SET no_delivery_days = :noDeliveryDays WHERE id_manufacturer = :manufacturerId;'; - $params = [ - 'manufacturerId' => $manufacturerId, - 'noDeliveryDays' => $noDeliveryDays - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $this->changeManufacturer($manufacturerId, 'no_delivery_days', $noDeliveryDays); } /** * @param int $productId * @param int $amount - * @return string */ protected function addProductToCart($productId, $amount) { @@ -256,7 +238,7 @@ protected function finishCart($general_terms_and_conditions_accepted = 1, $cance if ($comment != '') { $data['Carts']['pickup_day_entities'][0] = [ 'customer_id' => $this->getUserId(), - 'pickup_day' => !is_null($pickupDay) ? $pickupDay : Configure::read('app.timeHelper')->getDeliveryDateByCurrentDayForDb(), + 'pickup_day' => !is_null($pickupDay) ? $pickupDay : DeliveryRhythm::getDeliveryDateByCurrentDayForDb(), 'comment' => $comment, ]; } @@ -317,7 +299,14 @@ protected function changeProductPrice($productId, $price, $pricePerUnitEnabled = return $this->getJsonDecodedContent(); } - protected function changeProductDeliveryRhythm($productId, $deliveryRhythmType, $deliveryRhythmFirstDeliveryDay = '', $deliveryRhythmOrderPossibleUntil = '', $deliveryRhythmSendOrderListWeekday = '', $deliveryRhythmSendOrderListDay = '') + protected function changeProductDeliveryRhythm( + int $productId, + string $deliveryRhythmType, + string $deliveryRhythmFirstDeliveryDay = '', + string $deliveryRhythmOrderPossibleUntil = '', + string $deliveryRhythmSendOrderListWeekday = '', + string $deliveryRhythmSendOrderListDay = '' + ) { $this->ajaxPost('/admin/products/editDeliveryRhythm', [ 'productIds' => [$productId], @@ -352,27 +341,18 @@ protected function addPayment($customerId, $amount, $type, $manufacturerId = 0, return $this->getJsonDecodedContent(); } - - protected function changeManufacturer($manufacturerId, $field, $value) + protected function changeManufacturer(int $manufacturerId, string $field, $value) { - $query = 'UPDATE ' . $this->Manufacturer->getTable().' SET '.$field.' = :value WHERE id_manufacturer = :manufacturerId'; - $params = [ - 'value' => $value, - 'manufacturerId' => $manufacturerId - ]; - $statement = $this->dbConnection->prepare($query); - return $statement->execute($params); + $newManufacturer = $this->Manufacturer->get($manufacturerId); + $newManufacturer->{$field} = $value; + $this->Manufacturer->save($newManufacturer); } - protected function changeCustomer($customerId, $field, $value) + protected function changeCustomer(int $customerId, string $field, $value) { - $query = 'UPDATE ' . $this->Customer->getTable().' SET '.$field.' = :value WHERE id_customer = :customerId'; - $params = [ - 'value' => $value, - 'customerId' => $customerId - ]; - $statement = $this->dbConnection->prepare($query); - return $statement->execute($params); + $newCustomer = $this->Customer->get($customerId); + $newCustomer->{$field} = $value; + $this->Customer->save($newCustomer); } protected function getCorrectedLogoPathInHtmlForPdfs($html) @@ -392,16 +372,17 @@ protected function prepareSendingInvoices() protected function resetCustomerCreditBalance() { $this->Payment = $this->getTableLocator()->get('Payments'); - $this->dbConnection->execute('DELETE FROM ' . $this->Payment->getTable().' WHERE id = 2'); + $this->Payment->delete($this->Payment->get(2)); } private function prepareSendingOrderListsOrInvoices($contentFolder) { - $folder = new Folder(); - $folder->delete($contentFolder); - $file = new File($contentFolder . DS . '.gitignore', true); - $file->append('/* + Folder::rrmdir($contentFolder); + mkdir($contentFolder, 0755, true); + $file = fopen($contentFolder . DS . '.gitignore', 'w'); + fwrite($file, '/* !.gitignore'); + fclose($file); } } diff --git a/tests/TestCase/OrderDetailsControllerTestCase.php b/tests/TestCase/OrderDetailsControllerTestCase.php index bd13394571..2b32f75409 100644 --- a/tests/TestCase/OrderDetailsControllerTestCase.php +++ b/tests/TestCase/OrderDetailsControllerTestCase.php @@ -1,4 +1,6 @@ addProductToCart($this->productIdA, $productAAmount); diff --git a/tests/TestCase/Traits/AppIntegrationTestTrait.php b/tests/TestCase/Traits/AppIntegrationTestTrait.php index 921ee48595..c45816e87a 100644 --- a/tests/TestCase/Traits/AppIntegrationTestTrait.php +++ b/tests/TestCase/Traits/AppIntegrationTestTrait.php @@ -1,4 +1,6 @@ changeConfiguration('FCS_WEEKLY_PICKUP_DAY', 5); + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 1); + } + + protected function prepareWednesdayFridayConfig() + { + $this->changeConfiguration('FCS_WEEKLY_PICKUP_DAY', 5); + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 2); + } + + protected function prepareTuesdayFridayConfig() + { + $this->changeConfiguration('FCS_WEEKLY_PICKUP_DAY', 5); + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 3); + } + + protected function prepareMondayTuesdayConfig() + { + $this->changeConfiguration('FCS_WEEKLY_PICKUP_DAY', 2); + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 1); + } + + protected function prepareSaturdayThursdayConfig() + { + $this->changeConfiguration('FCS_WEEKLY_PICKUP_DAY', 4); + $this->changeConfiguration('FCS_DEFAULT_SEND_ORDER_LISTS_DAY_DELTA', 5); + $this->Product->updateAll(['delivery_rhythm_send_order_list_weekday' => 6], []); + } + +} diff --git a/tests/TestCase/Traits/LoginTrait.php b/tests/TestCase/Traits/LoginTrait.php index fcf49c0721..ad03af7f60 100644 --- a/tests/TestCase/Traits/LoginTrait.php +++ b/tests/TestCase/Traits/LoginTrait.php @@ -1,4 +1,5 @@ session($sessionData); } + public function loginAsMilkManufacturer() + { + $sessionData = $this->login(Configure::read('test.milkManufacturerId')); + $this->session($sessionData); + } + public function logout() { $this->get($this->Slug->getLogout()); diff --git a/tests/TestCase/Traits/PrepareAndTestInvoiceDataTrait.php b/tests/TestCase/Traits/PrepareAndTestInvoiceDataTrait.php index 0175b051dd..6ac30c058e 100644 --- a/tests/TestCase/Traits/PrepareAndTestInvoiceDataTrait.php +++ b/tests/TestCase/Traits/PrepareAndTestInvoiceDataTrait.php @@ -1,4 +1,5 @@ get('/admin/invoices/generate.pdf?customerId='.$customerId.'&paidInCash='.$paidInCash.'¤tDay=2018-02-02'); @@ -36,12 +39,13 @@ public function prepareOrdersAndPaymentsForInvoice($customerId) $this->finishCart(1, 1, '', null, $pickupDay); $this->OrderDetail = $this->getTableLocator()->get('OrderDetails'); - $query = 'UPDATE ' . $this->OrderDetail->getTable().' SET pickup_day = :pickupDay WHERE id_order_detail IN(4,5);'; - $params = [ - 'pickupDay' => $pickupDay, - ]; - $statement = $this->dbConnection->prepare($query); - $statement->execute($params); + $orderDetailEntityA = $this->OrderDetail->get(4); + $orderDetailEntityA->pickup_day = $pickupDay; + $this->OrderDetail->save($orderDetailEntityA); + + $orderDetailEntityB = $this->OrderDetail->get(5); + $orderDetailEntityB->pickup_day = $pickupDay; + $this->OrderDetail->save($orderDetailEntityB); $this->addPayment($customerId, 2.0, 'deposit', 0, '', $pickupDay); $this->addPayment($customerId, 3.2, 'deposit', 0, '', $pickupDay); @@ -51,9 +55,9 @@ public function prepareOrdersAndPaymentsForInvoice($customerId) public function doAssertInvoiceTaxes($data, $taxRate, $excl, $tax, $incl) { $this->assertEquals($data->tax_rate, $taxRate); - $this->assertEquals($data->total_price_tax_excl, $excl); - $this->assertEquals($data->total_price_tax, $tax); - $this->assertEquals($data->total_price_tax_incl, $incl); + $this->assertEquals(round($data->total_price_tax_excl, 2), $excl); + $this->assertEquals(round($data->total_price_tax, 2), $tax); + $this->assertEquals(round($data->total_price_tax_incl, 2), $incl); } public function getAndAssertOrderDetailsAfterCancellation($orderDetailIds) diff --git a/tests/TestCase/Traits/QueueTrait.php b/tests/TestCase/Traits/QueueTrait.php index 3738e30f19..05d504f57b 100644 --- a/tests/TestCase/Traits/QueueTrait.php +++ b/tests/TestCase/Traits/QueueTrait.php @@ -1,4 +1,5 @@ changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', '2019-11-01'); $this->exec('email_order_reminder 2019-10-27'); @@ -57,7 +63,7 @@ public function testGlobalDeliveryBreakEnabledAndNextDeliveryDay() $this->assertMailCount(0); } - public function testGlobalDeliveryBreakEnabledAndNotNextDeliveryDay() + public function testDeliveryBreakGlobalEnabledAndNotNextDeliveryDay() { $this->changeConfiguration('FCS_NO_DELIVERY_DAYS_GLOBAL', '2019-11-08'); $this->exec('email_order_reminder 2019-10-27'); @@ -87,8 +93,7 @@ public function testActiveOrder() public function testIfServiceNotSubscribed() { - $query = 'UPDATE '.$this->Customer->getTable().' SET email_order_reminder_enabled = 0;'; - $this->dbConnection->query($query); + $this->Customer->updateAll(['email_order_reminder_enabled' => 0], []); $this->exec('email_order_reminder'); $this->runAndAssertQueue(); $this->assertMailCount(0); diff --git a/tests/TestCase/src/Shell/PickupReminderShellTest.php b/tests/TestCase/src/Command/PickupReminderCommandTest.php similarity index 96% rename from tests/TestCase/src/Shell/PickupReminderShellTest.php rename to tests/TestCase/src/Command/PickupReminderCommandTest.php index 7c7c6979fa..3c1a67edd7 100644 --- a/tests/TestCase/src/Shell/PickupReminderShellTest.php +++ b/tests/TestCase/src/Command/PickupReminderCommandTest.php @@ -1,4 +1,5 @@ changeConfiguration('FCS_PURCHASE_PRICE_ENABLED', 1); diff --git a/tests/TestCase/src/Shell/SendInvoicesToCustomersShellTest.php b/tests/TestCase/src/Command/SendInvoicesToCustomersCommandTest.php similarity index 84% rename from tests/TestCase/src/Shell/SendInvoicesToCustomersShellTest.php rename to tests/TestCase/src/Command/SendInvoicesToCustomersCommandTest.php index 2398a8bee0..522fc8281f 100644 --- a/tests/TestCase/src/Shell/SendInvoicesToCustomersShellTest.php +++ b/tests/TestCase/src/Command/SendInvoicesToCustomersCommandTest.php @@ -1,4 +1,6 @@ changeConfiguration('FCS_SEND_INVOICES_TO_CUSTOMERS', 1); + $this->changeConfiguration('FCS_DEPOSIT_TAX_RATE', 0); + $this->loginAsSuperadmin(); + + $customerId = Configure::read('test.superadminId'); + $this->prepareOrdersAndPaymentsForInvoice($customerId); + + $this->get('/admin/invoices/preview.pdf?customerId='.$customerId.'&paidInCash=1¤tDay=2018-02-02&outputType=html'); + $this->assertResponseContains('
    Pfand geliefert1,00 €0,00 € (0%)1,00 €

    Demo Superadmin
    Demostrasse 4
    4644 Demostadt
    Tel.: 0600/000000

    Rechnung Nr.: xxx
    Rechnungsdatum: xx.xx.xxxx

    []

    Demo Superadmin
    Demostrasse 4
    4644 Demostadt
    Tel.: 0600/000000

    []

    Rechnung Nr.: xxx
    Rechnungsdatum: xx.xx.xxxx

    IDProduktHerstellerNettopreisSteuersatzLiefertag
    1x340BeuschlDemo Fleisch-Hersteller4,54 €10%02.02.2018
    1x347Forelle : Stück, 350gDemo Fleisch-Hersteller4,77 €10%02.02.2018
    3x348-11Rindfleisch, 1,5kgDemo Fleisch-Hersteller27,27 €10%02.02.2018
    1x346Artischocke : StückDemo Gemüse-Hersteller1,65 €10%02.02.2018
    1x60-10Milch : 0,5lDemo Milch-Hersteller0,55 €10%02.02.2018
    2xPfand geliefert0,84 €10%
    Nettosumme35,28 €
    Umsatzsteuer3,53 €
    Bruttosumme38,81 €
    [][][]ID[]Produkt[]Hersteller[]Nettopreis[]Steuersatz[]Liefertag[]1x[]340[]Beuschl[]Demo Fleisch-Hersteller[]4,54 €[]10%[]02.02.2018[]1x[]347[]Forelle : Stück, 350g[]Demo Fleisch-Hersteller[]4,77 €[]10%[]02.02.2018[]3x[]348-11[]Rindfleisch, 1,5kg[]Demo Fleisch-Hersteller[]27,27 €[]10%[]02.02.2018[]1x[]346[]Artischocke : Stück[]Demo Gemüse-Hersteller[]1,65 €[]10%[]02.02.2018[]1x[]60-10[]Milch : 0,5l[]Demo Milch-Hersteller[]0,55 €[]10%[]02.02.2018[]2x[][]Pfand geliefert[][]0,84 €[]10%[][][]Nettosumme[]35,28 €[][]Umsatzsteuer[]3,53 €[][]Bruttosumme[]38,81 €[]

    Übersichtstabelle nach Steuersatz

    IDProduktHerstellerNettopreisSteuersatzLiefertag
    SteuersatzSumme Preis exkl.Summe SteuerSumme Preis inkl.
    10%35,28 €3,53 €38,81 €
    []Steuersatz[]Summe Preis exkl.[]Summe Steuer[]Summe Preis inkl.[]10%[]35,28 €[]3,53 €[]38,81 €

    FoodCoop Test sagt Danke für Deinen Einkauf!

    Bar bezahlt am xx.xx.xxxx.

    \ No newline at end of file +


    FoodCoop Test
    Demostraße 4
    A-4564 Demostadt
    demo-foodcoop@maillinator.com

    Rechnung

    Demo Superadmin
    Demostrasse 4
    4644 Demostadt
    Tel.: 0600/000000

    Rechnung Nr.: xxx
    Rechnungsdatum: xx.xx.xxxx

    []

    Demo Superadmin
    Demostrasse 4
    4644 Demostadt
    Tel.: 0600/000000

    []

    Rechnung Nr.: xxx
    Rechnungsdatum: xx.xx.xxxx

    IDProduktHerstellerNettopreisSteuersatzLiefertag
    1x340BeuschlDemo Fleisch-Hersteller4,54 €10%02.02.2018
    1x347Forelle : Stück, 350gDemo Fleisch-Hersteller4,77 €10%02.02.2018
    3x348-11Rindfleisch, 1,5kgDemo Fleisch-Hersteller27,27 €10%02.02.2018
    1x346Artischocke : StückDemo Gemüse-Hersteller1,65 €10%02.02.2018
    1x60-10Milch : 0,5lDemo Milch-Hersteller0,55 €10%02.02.2018
    2xPfand geliefert0,90 €10%
    Nettosumme34,95 €
    Umsatzsteuer3,50 €
    Bruttosumme38,45 €
    [][][]ID[]Produkt[]Hersteller[]Nettopreis[]Steuersatz[]Liefertag[]1x[]340[]Beuschl[]Demo Fleisch-Hersteller[]4,54 €[]10%[]02.02.2018[]1x[]347[]Forelle : Stück, 350g[]Demo Fleisch-Hersteller[]4,77 €[]10%[]02.02.2018[]3x[]348-11[]Rindfleisch, 1,5kg[]Demo Fleisch-Hersteller[]27,27 €[]10%[]02.02.2018[]1x[]346[]Artischocke : Stück[]Demo Gemüse-Hersteller[]1,65 €[]10%[]02.02.2018[]1x[]60-10[]Milch : 0,5l[]Demo Milch-Hersteller[]0,55 €[]10%[]02.02.2018[]2x[][]Pfand geliefert[][]0,90 €[]10%[][][]Nettosumme[]34,95 €[][]Umsatzsteuer[]3,50 €[][]Bruttosumme[]38,45 €[]

    Übersichtstabelle nach Steuersatz

    IDProduktHerstellerNettopreisSteuersatzLiefertag
    SteuersatzSumme Preis exkl.Summe SteuerSumme Preis inkl.
    10%34,95 €3,50 €38,45 €
    []Steuersatz[]Summe Preis exkl.[]Summe Steuer[]Summe Preis inkl.[]10%[]34,95 €[]3,50 €[]38,45 €

    FoodCoop Test sagt Danke für Deinen Einkauf!

    Bar bezahlt am xx.xx.xxxx.

    \ No newline at end of file diff --git a/tests/config/test.config.php b/tests/config/test.config.php index 20540d44ca..e5784db386 100644 --- a/tests/config/test.config.php +++ b/tests/config/test.config.php @@ -1,4 +1,6 @@ + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ +body.dark { + background-color: var(--dark-mode-main-bg-color); + color: var(--dark-mode-main-font-color); +} +body.dark img { + filter: brightness(.8) contrast(1.2); +} +body.dark #header, +body.dark #content, +body.dark .sidebar, +body.dark .box, +body.dark #inner-content, +body.dark .modal-content, +body.dark .btn-outline-light.disabled:not(.btn-danger), +body.dark .self-service .header, +body.dark .self-service #SelfServiceForm, +body.dark .self-service #login-form h2 span, +body.dark.admin #menu, +body.dark.admin .filter-container, +body.dark.admin table.list, +body.dark.admin #actionLogs div.changed, +body.dark table.list td.image, +body.dark .self-service .right-box { + background-color: var(--dark-mode-main-bg-color); +} +body.dark .ui-widget-header, +body.dark .ui-state-default, +body.dark .ui-widget-header .ui-state-hover { + color: var(--dark-mode-main-font-color); + border-color: var(--dark-mode-main-border-color); + background-color: var(--dark-mode-main-bg-color); + background-image: none; +} +body.dark #main-menu li ul, +body.dark #user-menu li ul, +body.dark .bootstrap-select .dropdown-toggle, +body.dark .bootstrap-select .dropdown-menu, +body.dark .bootstrap-select .no-results, +body.dark form.sync-login-form, +body.dark.syncs.product_data table.product-list tr.horizontal-checkboxes, +body.dark .pw.delivery-break-enabled { + background-color: var(--dark-mode-2nd-bg-color); + border-color: var(--dark-mode-main-border-color); +} +body.dark .vertical.menu a { + background-color: var(--dark-mode-main-bg-color) ! important; +} +body.dark .modal-content { + border: 1px solid var(--dark-mode-main-border-color); +} +body.dark .btn-outline-light:not(.btn-danger), +body.dark select, +body.dark #product-search input, +body.dark #product-search select, +body.dark input, +body.dark .pw .amount-wrapper input, +body.dark textarea, +body.dark .bootstrap-select .form-control:focus, +body.dark.admin #home h1, +body.dark.admin ul.nav-tabs, +body.dark .ui-widget.ui-widget-content { + background-color: var(--dark-mode-2nd-bg-color); + color: var(--dark-mode-main-font-color); + border-color: var(--dark-mode-main-border-color); +} +body.dark #footer, +body.dark .self-service .footer { + background-color: var(--dark-mode-2nd-bg-color); + border-color: var(--dark-mode-main-border-color); +} +body.dark .btn-outline-light:hover, +body.dark .btn-outline-light:active:focus { + background-color: var(--dark-mode-2nd-bg-color) ! important; + opacity: .7; +} +body.dark .cookieConsentWrapper { + background-color: var(--dark-mode-2nd-bg-color) ! important; + color: var(--dark-mode-main-font-color) ! important; +} +.cookieConsentWrapper .cookieConsent__Right button { + color: var(--dark-mode-2nd-font-color) ! important; +} +body.dark .testimonial-quote blockquote, +body.dark .testimonial-quote cite span { + text-shadow: none; +} +body.dark #main-menu li > a, +body.dark #user-menu li > a, +body.dark a.blog-post-wrapper span, +body.dark .cart span.product-name-wrapper span.unity, +body.dark .modal-title, +body.dark .modal-header i, +body.dark #footer a:hover, +body.dark .bootstrap-select .filter-option-inner-inner, +body.dark .bootstrap-select .dropdown-menu li a span.text, +body.dark .bootstrap-select .no-results, +body.dark .bootstrap-select > .dropdown-toggle:after, +body.dark.admin .filter-container, +body.dark.admin a.btn i:not(.not-ok), +body.dark a.btn.edit-shortcut-button i { + color: var(--dark-mode-main-font-color); +} +body.dark h2.warning, +body.dark h2.info, +body.dark h2.info2, +body.dark .box h3, +body.dark .box h3 i, +body.dark .cart-extra-info, +body.dark .vertical.menu li.header, +body.dark #footer .bottom a i, +body.dark #footer .bottom a, +body.dark .btn-success { + color: var(--dark-mode-2nd-font-color); +} +body.dark .vertical.menu a, +body.dark.admin.modal-open .modal a:not(.btn):not(.upload-button), +body.dark.admin table.list a:not(.btn), +body.dark.admin h2 span a, +body.dark.admin form .input a:not(.btn), +body.dark .tooltipster-box .tooltipster-content, +body.dark .tooltipster-box .tooltipster-content a, +body.dark table.list td.not-available, +body.dark table.list td.not-available i.sold-out-limit-for-dialog i, +body.dark.admin ul.nav-tabs > li > a { + color: var(--dark-mode-main-font-color) ! important; +} +body.dark a.blog-post-wrapper, +body.dark .vertical.menu, +body.dark.admin div:not(.product-list) > table.list tr:nth-child(odd), +body.dark.admin table.list tr.custom-odd, +body.dark.admin table.list th, +body.dark .tooltipster-box .tooltipster-content, +body.dark.reports.payment form#csv-records tr.not-selected { + background-color: var(--dark-mode-2nd-bg-color); +} +body.dark .vertical.menu a.active, +body.dark .vertical.menu a:hover, +body.dark .vertical.menu a:hover i, +body.dark .vertical.menu a.active i, +body.dark .vertical.menu a.active span.additional-info, +body.dark .vertical.menu a:hover span.additional-info { + color: var(--dark-mode-2nd-font-color) ! important; +} +body.dark.admin ul.nav-tabs li.active a, +body.dark.admin ul.nav-tabs li a:hover { + background-color: var(--dark-mode-main-bg-color) ! important; + +} +body.dark a.blog-post-wrapper:hover { + opacity: .8; +} +body.dark .swiper-button-prev:after, +body.dark .swiper-button-next:after, +body.dark .btn-warning { + color: var(--dark-mode-main-bg-color); +} +body.dark.admin table.list th { + box-shadow: inset 0 0px 0 var(--dark-mode-main-border-color), inset 0 -1px 0 var(--dark-mode-main-border-color); +} +body.dark .dropdown-item:hover span, +body.dark .btn-warning:hover { + color: var(--dark-mode-main-bg-color) ! important; +} +body.dark .dropdown-item.active:hover span, +body.dark .btn-warning.active:hover { + color: #fff ! important; +} +body.dark .box, +body.dark #header, +body.dark .vertical.menu, +body.dark div.c2, +body.dark div.c3, +body.dark .vertical.menu, +body.dark .vertical.menu li.header, +body.dark .vertical.menu a, +body.dark #content, +body.dark.carts .cart:not(#cart) .product, +body.dark #cart p.product-sum-wrapper, +body.dark #cart p.total-sum-wrapper, +body.dark #right .inner-right, +body.dark .pw, +body.dark .manufacturer-wrapper, +body.dark a.blog-post-wrapper h3, +body.dark .modal-footer, +body.dark .h1-registration, +body.dark .self-service #SelfServiceForm, +body.dark .self-service #cart, +body.dark .self-service #login-form h2, +body.dark.admin .filter-container, +body.dark.admin table.list th, +body.dark.admin table.list td, +body.dark.admin table.list tr, +body.reports.payment form#csv-upload { + border-color: var(--dark-mode-main-border-color); +} +body.dark #load-last-order-details { + border-color: var(--dark-mode-main-border-color) ! important; +} +body.dark .btn-outline-light:active { + color: #fff; +} +body.dark .menu.vertical span.additional-info { + background-color: transparent ! important; + color: var(--dark-mode-main-font-color) ! important; +} +body.dark .menu.vertical a:hover i, +body.dark .menu.vertical a.active i, +body.dark.admin .vertical.menu > li img.logo { + background-color: transparent ! important; +} +body.dark ul.menu.vertical:not(#user-menu):not(#footer-menu) a:hover, +body.dark ul.menu.vertical:not(#user-menu):not(#footer-menu) a.active, +body.dark #footer a:hover, +body.dark .ui-datepicker-calendar .ui-state-active { + text-decoration: underline ! important; +} +body.dark .blog-wrapper .swiper-button-prev, +body.dark .blog-wrapper .swiper-button-next, +body.dark #scroll-to-top a { + background-color: var(--dark-mode-main-font-color); +} +body.dark.admin table.list:not(.no-hover) tr:not(:last-child):not(:first-child):hover, +body.dark.admin table.list tr.selected { + background-color: #43464B ! important; +} + diff --git a/webroot/css/frontend.css b/webroot/css/frontend.css index 104f3185e4..4bea84aebe 100644 --- a/webroot/css/frontend.css +++ b/webroot/css/frontend.css @@ -29,11 +29,11 @@ body { z-index: 100; margin-right: 0px; border-right: 1px solid #d6d4d4; - margin-top: 148px; + padding-top: 148px; } #inner-content { float: left; - width: 736px; + width: 737px; background: #fff; margin-top: 20px; padding-right: 10px; @@ -132,9 +132,14 @@ body.categories.detail div.description-wrapper { color: #fff; border-bottom: 1px solid #dfdfdf; font-weight: 600; + border-top-right-radius: 5px; +} +.vertical.menu li:last-of-type a { + border-bottom-right-radius: 3px; } .vertical.menu a { padding: 4px 10px; + word-break: break-word; } .vertical.menu a i.fas { font-size: 13px; @@ -166,25 +171,25 @@ body.categories.detail div.description-wrapper { left: 241px; top: 137px; width: 670px; - padding: 24px 15px 0 15px; + padding: 15px 15px 0 15px; background: #fff; border: 1px solid #d6d4d4; z-index: 1000; display: none; + border-radius: 5px; + columns: 3; + min-height: 49px; } #main-menu li ul ul { display: none ! important; /* never show third level */ } -#main-menu li ul li { - float: left; - line-height: 17px; - width: 201px; - margin-left: 16px; -} #main-menu li ul li a { + line-height: 16px; + margin-left: 16px; margin-top: 0px; - margin-bottom: 7px; + margin-bottom: 5px; float: left; + width: 201px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -218,9 +223,14 @@ body.categories.detail div.description-wrapper { #user-menu li > a { color: #333; } -#user-menu i.fa-user { +#user-menu i.fa-user, +.color-mode-toggle i { padding-right: 5px; } +.color-mode-toggle { + margin-top: 1px; + float: left; +} #user-menu > li { padding-bottom: 5px; } @@ -256,24 +266,16 @@ body.categories.detail div.description-wrapper { margin: 0 auto; } #login-form h1 { - border: 1px solid #fff; - border-bottom: none; line-height: 30px; margin-bottom: 0px; padding: 10px; padding-left: 108px; - -moz-border-radius: 0px; - -webkit-border-radius: 10px 10px 0px 0px; - border-radius: 10px 10px 0px 0px; } #inner-content #login-form form, body.customers.new_password_request #inner-content form { border-top: none; float: left; padding: 10px; - -moz-border-radius: 0px; - -webkit-border-radius: 0px 0px 10px 10px; - border-radius: 0px 0px 10px 10px; } body.customers.new_password_request #inner-content form { padding: 0; @@ -333,7 +335,7 @@ body.customers.new_password_request .input { body.customers.new_password_request .input input { width: 255px; padding: 3px; - border: 1px solid #ccc; + border-radius: 3px; } body.customers.new_password_request .input { margin-right: 10px; @@ -423,11 +425,22 @@ body.superadmin .pw .heading h4 { } .manufacturer-wrapper div.c1 img { width: 148px; + border-radius: 5px; } .pw div.c1 span.fa-stack i.fa-star { color: #fff; } -.pw div.c3 .unity, +.pw.delivery-break-enabled { + background-color: #efefef; + opacity: .7; +} +.pw.delivery-break-enabled .descriptions { + display: none; +} +body.products.detail .pw.delivery-break-enabled .descriptions { + display: block; +} + .pw div.c3 .unity, .pw .radio label { word-break: break-word; } @@ -444,7 +457,7 @@ body.superadmin .pw .heading h4 { .manufacturer-wrapper div.c2 { float: left; width: 355px; - min-height: 170px; + min-height: 113px; padding-right: 15px; margin-right: 15px; border-right: 1px solid #d6d4d4; @@ -539,6 +552,7 @@ body.superadmin .pw .heading h4 { margin-top: 5px; margin-bottom: 10px; float: left; + width: 100%; } .pw .amount-wrapper .loi { float: left; @@ -563,14 +577,29 @@ body.superadmin .pw .heading h4 { .pw .amount-wrapper input { float:left; text-align: center; - height: 28px; + height: 26px; + width: 26px; border: 1px solid #d6d4d4; - width: 28px; + border-radius: 3px; } -.pw .amount-wrapper .right-of-input { - float: left; +.pw .amount-wrapper .below-input { + float: right; clear: both; } +.pw .c3 i.is-stock-product { + float: right; +} +.pw .c3 i.is-stock-product { + margin-right: 3px; +} +.pw .amount-wrapper .ordered-products-total-amount { + float: right; + border-radius: 50%; + border: 1px solid var(--not-ok-red); + text-align: center; + width: 30px; + height: 30px; +} .pw span.not-available-info { display: block; margin-top: 10px; @@ -616,6 +645,7 @@ body.manufacturers.index h2.info { width: 928px; min-height: 172px; float: left; + border-right: 1px solid #d6d4d4; } #footer .inner-footer { padding: 20px; @@ -698,9 +728,10 @@ body.manufacturers.index h2.info { } .box { left: 927px; - width: 305px; + width: 306px; background: #fff; border-bottom: 1px solid #d6d4d4; + border-right: 1px solid #d6d4d4; } .box h3 { color: #fff; @@ -717,6 +748,9 @@ body.manufacturers.index h2.info { padding: 5px; padding-bottom: 0px; } +.box:last-of-type { + border-bottom-right-radius: 3px; +} #cart { min-height: 140px; } @@ -926,7 +960,7 @@ body.carts #CartsDetailForm { .h1-registration { border-radius: 0; margin-top: 20px; - padding-top: 20px; + padding-top: 25px ! important; border-top: 1px solid #d6d4d4; } #CartsDetailForm .input.checkbox .error-message, @@ -1097,11 +1131,15 @@ body.carts .cart:not(#cart) .btn-success.btn-order { padding: 6px; background: #fbfbfb; line-height: 1.5; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; } #product-search .btn { padding: 6px 10px; border-radius: 0; float: right; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; } #product-search a.btn { margin-left: 1px; @@ -1135,10 +1173,9 @@ a.blog-post-wrapper:nth-of-type(3n) { } a.blog-post-wrapper img { padding: 2px; - width: 100%; - object-fit: contain; - height: 145px; - margin-bottom: 3px; + margin-bottom: 5px; + max-width: 100%; + max-height: 100%; } a.blog-post-wrapper h3 { font-size: 16px; @@ -1156,9 +1193,10 @@ a.blog-post-wrapper span { float: left; line-height: 19px; color: #333; + word-break: break-word; } a.blog-post-wrapper span.content { - max-height: 107px; + max-height: 105px; overflow-y: hidden; } a.blog-post-wrapper span.date { @@ -1169,6 +1207,10 @@ a.blog-post-wrapper span.date { } a.blog-post-wrapper span.img-wrapper { float: none; + display: flex; + align-items: center; + justify-content: center; + height: 55%; } a.blog-post-wrapper:hover { box-shadow: 8px 10px 15px 0px rgba(0,0,0,0.5); @@ -1227,7 +1269,6 @@ body.blog_posts.detail #inner-content .content h2, body.pages:not(.home) #inner-content h2, body.manufacturers.detail .manufacturer-infos h2, body.categories.detail .description-wrapper h2 { - color: #333; padding-top: 10px; font-size: 18px; } @@ -1296,9 +1337,8 @@ body.blog_posts.detail p.neighbors a { .blog-wrapper .swiper-button-prev, .blog-wrapper .swiper-button-next { background-color: #fff; - border-radius: 22px; } body.feedbacks.index .edit-shortcut-button { position: absolute; right: 10px; -} \ No newline at end of file +} diff --git a/webroot/css/global.css b/webroot/css/global.css index 441bc92628..ecadd0acea 100644 --- a/webroot/css/global.css +++ b/webroot/css/global.css @@ -11,6 +11,16 @@ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com * @link https://www.foodcoopshop.com */ + + :root { + --dark-mode-main-bg-color: #0f0f0f; + --dark-mode-2nd-bg-color: #1b1b1b; + --dark-mode-main-font-color: #CDCDCD; + --dark-mode-2nd-font-color: #f4f0ec;; + --dark-mode-main-border-color: #696969; + --not-ok-red: #dc143c; +} + @media print { a:link:after, a:visited:after { content: ''; @@ -20,8 +30,10 @@ body { font-family: 'Open Sans'; color: #333333; background-attachment: fixed; - background-color: #e6e6e6; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600' viewBox='0 0 600 600'%3E%3Cpath fill='%23cccccc' fill-opacity='0.33' d='M600 325.1v-1.17c-6.5 3.83-13.06 7.64-14.68 8.64-10.6 6.56-18.57 12.56-24.68 19.09-5.58 5.95-12.44 10.06-22.42 14.15-1.45.6-2.96 1.2-4.83 1.9l-4.75 1.82c-9.78 3.75-14.8 6.27-18.98 10.1-4.23 3.88-9.65 6.6-16.77 8.84-1.95.6-3.99 1.17-6.47 1.8l-6.14 1.53c-5.29 1.35-8.3 2.37-10.54 3.78-3.08 1.92-6.63 3.26-12.74 5.03a384.1 384.1 0 0 1-4.82 1.36c-2.04.58-3.6 1.04-5.17 1.52a110.03 110.03 0 0 0-11.2 4.05c-2.7 1.15-5.5 3.93-8.78 8.4a157.68 157.68 0 0 0-6.15 9.2c-5.75 9.07-7.58 11.74-10.24 14.51a50.97 50.97 0 0 1-4.6 4.22c-2.33 1.9-10.39 7.54-11.81 8.74a14.68 14.68 0 0 0-3.67 4.15c-1.24 2.3-1.9 4.57-2.78 8.87-2.17 10.61-3.52 14.81-8.2 22.1-4.07 6.33-6.8 9.88-9.83 12.99-.47.48-.95.96-1.5 1.48l-3.75 3.56c-1.67 1.6-3.18 3.12-4.86 4.9a42.44 42.44 0 0 0-9.89 16.94c-2.5 8.13-2.72 15.47-1.76 27.22.47 5.82.51 6.36.51 8.18 0 10.51.12 17.53.63 25.78.24 4.05.56 7.8.97 11.22h.9c-1.13-9.58-1.5-21.83-1.5-37 0-1.86-.04-2.4-.52-8.26-.94-11.63-.72-18.87 1.73-26.85a41.44 41.44 0 0 1 9.65-16.55c1.67-1.76 3.18-3.27 4.83-4.85.63-.6 3.13-2.96 3.75-3.57a71.6 71.6 0 0 0 1.52-1.5c3.09-3.16 5.86-6.76 9.96-13.15 4.77-7.42 6.15-11.71 8.34-22.44.86-4.21 1.5-6.4 2.68-8.6.68-1.25 1.79-2.48 3.43-3.86 1.38-1.15 9.43-6.8 11.8-8.72 1.71-1.4 3.26-2.81 4.7-4.3 2.72-2.85 4.56-5.54 10.36-14.67a156.9 156.9 0 0 1 6.1-9.15c3.2-4.33 5.9-7.01 8.37-8.07 3.5-1.5 7.06-2.77 11.1-4.02a233.84 233.84 0 0 1 7.6-2.2l2.38-.67c6.19-1.79 9.81-3.16 12.98-5.15 2.14-1.33 5.08-2.33 10.27-3.65l6.14-1.53c2.5-.63 4.55-1.2 6.52-1.82 7.24-2.27 12.79-5.06 17.15-9.05 4.05-3.72 9-6.2 18.66-9.9l4.75-1.82c1.87-.72 3.39-1.31 4.85-1.91 10.1-4.15 17.07-8.32 22.76-14.4 6.05-6.45 13.95-12.4 24.49-18.92 1.56-.96 7.82-4.6 14.15-8.33v-64.58c-4 8.15-8.52 14.85-12.7 17.9-2.51 1.82-5.38 4.02-9.04 6.92a1063.87 1063.87 0 0 0-6.23 4.98l-1.27 1.02a2309.25 2309.25 0 0 1-4.87 3.9c-7.55 6-12.9 10.05-17.61 13.19-3.1 2.06-3.86 2.78-8.06 7.13-5.84 6.07-11.72 8.62-29.15 10.95-11.3 1.5-20.04 4.91-30.75 11.07-1.65.94-7.27 4.27-6.97 4.1-2.7 1.58-4.69 2.69-6.64 3.66-5.63 2.8-10.47 4.17-15.71 4.17-17.13 0-41.44 11.51-51.63 22.83-12.05 13.4-31.42 27.7-45.25 31.16-7.4 1.85-11.85 7.05-14.04 14.69-1.26 4.4-1.58 8.28-1.58 13.82 0 .82.01.98.24 3.63.45 5.18.35 8.72-.77 13.26-1.53 6.2-4.89 12.6-10.59 19.43-13.87 16.65-22.88 46.58-22.88 71.68 0 2.39.02 4.26.06 8.75.12 10.8.1 15.8-.22 21.95-.56 11.18-2.09 20.73-5 29.3h-1.05c2.94-8.56 4.49-18.12 5.05-29.35.31-6.13.34-11.1.22-21.9-.04-4.48-.06-6.36-.06-8.75 0-25.32 9.07-55.47 23.12-72.32 5.6-6.72 8.88-12.99 10.38-19.03 1.09-4.4 1.18-7.85.74-12.93-.23-2.7-.24-2.86-.24-3.72 0-5.62.32-9.57 1.62-14.1 2.28-7.95 6.97-13.44 14.76-15.39 13.6-3.4 32.82-17.59 44.75-30.84C409 360.14 433.58 348.5 451 348.5c5.07 0 9.77-1.33 15.26-4.07 1.93-.96 3.9-2.05 6.58-3.62-.3.18 5.33-3.16 6.98-4.11 10.82-6.21 19.66-9.67 31.11-11.2 17.23-2.3 22.9-4.75 28.57-10.64 4.25-4.41 5.04-5.16 8.22-7.28 4.68-3.11 10.01-7.14 17.55-13.14a1113.33 1113.33 0 0 0 4.86-3.89l1.28-1.02a4668.54 4668.54 0 0 1 6.23-4.98c3.67-2.9 6.55-5.12 9.07-6.95 4.37-3.19 9.16-10.56 13.29-19.4v66.9zm0-116.23c-.62.01-1.27.06-1.95.13-6.13.63-13.83 3.45-21.83 7.45-3.64 1.82-8.46 2.67-14.17 2.71-4.7.04-9.72-.47-14.73-1.33-1.7-.3-3.26-.61-4.67-.93a31.55 31.55 0 0 0-3.55-.57 273.4 273.4 0 0 0-16.66-.88c-10.42-.16-17.2.74-17.97 2.73-.38.97.6 2.55 3.03 4.87 1.01.97 2.22 2.03 4.04 3.55a1746.07 1746.07 0 0 0 4.79 4.02c1.39 1.2 3.1 1.92 5.5 2.5.7.16.86.2 2.64.54 3.53.7 5.03 1.25 6.15 2.63 1.41 1.76 1.4 4.54-.15 8.88-2.44 6.83-5.72 10.05-10.19 10.33-3.63.23-7.6-1.29-14.52-5.06-4.53-2.47-6.82-7.3-8.32-15.26-.17-.87-.32-1.78-.5-2.86l-.43-2.76c-1.05-6.58-1.9-9.2-3.73-10.11-.81-.4-1.59-.74-2.36-1-2.27-.77-4.6-1.02-8.1-.92-2.29.07-14.7 1-13.77.93-20.55 1.37-28.8 5.05-37.09 14.99a133.07 133.07 0 0 0-4.25 5.44l-2.3 3.09-2.51 3.32c-4.1 5.36-7.06 8.48-10.39 11.12-.65.52-1.33 1.04-2.13 1.62l-4.11 2.94a106.8 106.8 0 0 0-5.16 3.99c-4.55 3.74-9.74 8.6-16.25 15.38-8.25 8.58-11.78 13.54-11.7 15.95.07 1.65 1.64 2.11 6.79 2.38 1.61.09 2.15.12 2.98.2 2.95.24 5.09.73 6.81 1.68 7.48 4.15 11.63 7.26 13.95 11.58 3.3 6.15.8 12.88-8.89 20.26-8.28 6.3-11.1 10.37-11.31 14.96-.06 1.17 0 1.93.26 4.43.69 6.47.25 10.65-2.8 17.42a44.23 44.23 0 0 1-4.16 7.53c-2.82 3.97-5.47 5.74-10.6 7.69-.43.16-3.34 1.23-4.27 1.59-1.8.68-3.38 1.36-5.01 2.14-4.18 2-8.4 4.6-13.1 8.24-8.44 6.51-13.23 14.56-15.98 25.06-1.1 4.2-1.55 6.81-2.8 15.21-1.26 8.6-2.17 12.64-4.08 16.55-2.1 4.28-11.93 26.59-12.97 28.88a382.7 382.7 0 0 1-6.37 13.41c-4.07 8.11-7.61 14.07-10.73 17.81-5.38 6.46-8.98 14.37-13.77 28.42a810.14 810.14 0 0 0-1.89 5.6c-1.8 5.35-2.96 8.6-4.26 11.85-6.13 15.32-25.43 26.31-46.46 26.31-11.2 0-20.58-2.74-31.02-8.55-5.6-3.13-4.55-2.42-22.26-14.54-14.33-9.8-17.7-10.73-20.47-6.9-.37.5-1.81 2.74-1.83 2.77a52.24 52.24 0 0 1-4.94 5.9c-.73.79-5.52 5.87-6.97 7.45-2.38 2.6-4.3 4.81-5.98 6.93a45.6 45.6 0 0 0-5.08 7.66c-1.29 2.57-1.9 5.25-2.66 10.6a997.6 997.6 0 0 1-.46 3.18h-1l.47-3.32c.77-5.45 1.4-8.2 2.75-10.9a46.54 46.54 0 0 1 5.2-7.84c1.7-2.14 3.63-4.38 6.03-6.98 1.45-1.59 6.24-6.68 6.96-7.46a51.58 51.58 0 0 0 4.84-5.78s1.47-2.26 1.86-2.8c3.25-4.5 7.08-3.44 21.84 6.67 17.67 12.08 16.62 11.38 22.19 14.48 10.3 5.73 19.5 8.43 30.53 8.43 20.65 0 39.57-10.77 45.54-25.69a219.7 219.7 0 0 0 4.24-11.8 6752.32 6752.32 0 0 0 1.88-5.6c4.83-14.16 8.47-22.14 13.96-28.73 3.05-3.66 6.56-9.57 10.6-17.61 1.97-3.93 4.04-8.31 6.35-13.38 1.03-2.28 10.88-24.61 12.98-28.91 1.85-3.79 2.75-7.76 4-16.25 1.24-8.44 1.7-11.07 2.81-15.32 2.8-10.7 7.71-18.94 16.33-25.6a73.18 73.18 0 0 1 13.29-8.35c1.66-.8 3.27-1.48 5.08-2.18.94-.36 3.86-1.43 4.28-1.59 4.95-1.88 7.44-3.55 10.14-7.33 1.35-1.9 2.68-4.3 4.06-7.37 2.97-6.58 3.39-10.59 2.72-16.9a27.13 27.13 0 0 1-.27-4.58c.22-4.94 3.21-9.24 11.7-15.7 9.33-7.11 11.66-13.34 8.62-19-2.2-4.09-6.25-7.12-13.55-11.17-1.57-.88-3.6-1.33-6.42-1.57-.8-.07-1.34-.1-2.95-.19-5.77-.3-7.63-.85-7.72-3.34-.1-2.81 3.5-7.87 11.97-16.69 6.53-6.8 11.75-11.69 16.33-15.45 1.79-1.47 3.42-2.72 5.2-4.03l4.12-2.94c.79-.58 1.46-1.08 2.1-1.59 3.26-2.6 6.16-5.65 10.21-10.94a383.2 383.2 0 0 0 2.5-3.32l2.31-3.09c1.8-2.39 3.04-4 4.29-5.48 8.47-10.17 16.98-13.96 37.27-15.3-.44.02 12-.9 14.32-.98 3.62-.1 6.05.16 8.46.98.8.27 1.62.62 2.47 1.04 2.27 1.14 3.17 3.87 4.27 10.85l.44 2.76c.17 1.07.33 1.97.5 2.83 1.44 7.69 3.62 12.29 7.8 14.57 6.76 3.68 10.6 5.15 13.99 4.94 4-.25 6.99-3.17 9.3-9.67 1.45-4.04 1.46-6.49.32-7.92-.9-1.12-2.28-1.62-5.57-2.27a55.8 55.8 0 0 1-2.67-.55c-2.54-.6-4.39-1.4-5.93-2.71a252.63 252.63 0 0 0-4.78-4.01 84.35 84.35 0 0 1-4.08-3.6c-2.73-2.6-3.86-4.43-3.28-5.95 1.02-2.64 7.82-3.54 18.93-3.37a230.56 230.56 0 0 1 16.73.88c2.76.39 3.2.49 3.68.6 1.4.3 2.95.62 4.62.91a82.9 82.9 0 0 0 14.56 1.32c5.56-.04 10.24-.86 13.73-2.6 8.1-4.05 15.89-6.9 22.17-7.56.7-.07 1.4-.11 2.05-.13v1zm0-100.94v1.5c-8.62 16.05-17.27 29.55-23.65 35.92-3.19 3.2-7.62 4.9-13.54 5.56-4.45.48-8.28.4-19.18-.2-9.91-.55-15.32-.44-20.52.78a84.05 84.05 0 0 1-15 2.11l-2.25.14c-12.49.75-19.37 1.78-32.72 5.74-4.5 1.33-9.27 2.49-14.3 3.48a246.27 246.27 0 0 1-32.6 3.97c-7.56.45-13.21.57-20.24.57-5.4 0-11.9 1.61-18 5.18-8.3 4.87-15.06 12.87-19.53 24.5a68.57 68.57 0 0 1-4.56 9.8c-3.6 6.2-6.92 8.99-13.38 12.18l-4.03 1.96a64.48 64.48 0 0 0-15.16 10.25c-8.2 7.33-13.72 16.63-22.54 35.6l-2.08 4.49c-7.3 15.7-11.5 23.3-17.35 29.87-7.7 8.66-20.25 14.42-40.31 20.08-4.37 1.23-19.04 5.08-19.24 5.13-6.92 1.87-11.68 3.34-15.63 4.92-10.55 4.22-18.71 10.52-36.38 26.52l-1.7 1.54c-8.58 7.76-13.41 11.9-18.81 15.88-3.95 2.9-8 5.67-12.97 8.91-2.06 1.34-10.3 6.6-12.33 7.94-11.52 7.5-18.53 13.04-24.62 20.08a62.01 62.01 0 0 0-6.44 8.85c-4.13 6.91-6.27 13.15-9.2 25.11l-1.54 6.26c-.6 2.45-1.15 4.54-1.72 6.58-2.97 10.7-6.9 17.36-14.78 26.91L69.6 491a148.51 148.51 0 0 0-4.19 5.3 23.9 23.9 0 0 0-3.44 6.28c-1.16 3.23-1.52 5.9-1.87 11.94-.58 10.05-1.42 15.04-4.63 22.67-1.57 3.72-5.66 14.02-6.41 15.8a73.46 73.46 0 0 1-3.57 7.4c-2.88 5.14-6.71 10.12-13.12 16.95-5.96 6.36-8.87 10.9-10.61 16a56.88 56.88 0 0 0-1.38 4.82l-.46 1.84h-1.03l.52-2.08c.52-2.09.92-3.49 1.4-4.9 1.8-5.25 4.78-9.9 10.84-16.36 6.35-6.78 10.13-11.7 12.97-16.77a72.5 72.5 0 0 0 3.52-7.29c.75-1.76 4.84-12.06 6.4-15.8 3.17-7.5 3.99-12.4 4.56-22.33.35-6.14.72-8.88 1.93-12.23a24.9 24.9 0 0 1 3.58-6.54c1.27-1.7 2.6-3.37 4.22-5.34l4.11-4.95c7.8-9.46 11.66-16 14.59-26.54.56-2.04 1.1-4.12 1.71-6.56l1.53-6.26c2.96-12.04 5.13-18.36 9.32-25.39 1.84-3.08 4-6.05 6.54-8.99 6.17-7.12 13.24-12.7 24.83-20.26 2.05-1.33 10.28-6.6 12.33-7.94 4.96-3.22 9-5.98 12.92-8.87 5.37-3.95 10.19-8.08 18.74-15.82l1.7-1.54c17.76-16.09 25.98-22.43 36.67-26.7 4-1.6 8.8-3.09 15.75-4.96.21-.06 14.87-3.9 19.22-5.13 19.9-5.61 32.32-11.31 39.85-19.78 5.76-6.48 9.93-14.02 17.18-29.64l2.09-4.5c8.87-19.07 14.44-28.46 22.77-35.9a65.48 65.48 0 0 1 15.38-10.4l4.04-1.97c6.3-3.1 9.47-5.77 12.96-11.77a67.6 67.6 0 0 0 4.48-9.67c4.56-11.84 11.47-20.02 19.97-25 6.25-3.66 12.93-5.32 18.5-5.32 7.01 0 12.65-.12 20.17-.57a245.3 245.3 0 0 0 32.47-3.96c5-.98 9.75-2.13 14.22-3.45 13.43-3.98 20.38-5.02 32.94-5.78l2.24-.14c5.76-.37 9.8-.9 14.85-2.09 5.31-1.25 10.79-1.35 22.6-.7 9.04.5 12.84.58 17.21.1 5.71-.62 9.94-2.26 12.95-5.26 6.44-6.45 15.3-20.37 24.35-36.72zm0 450.21c-1.28-4.6-2.2-10.55-3.33-20.25l-.24-2.04-.23-2.03c-1.82-15.7-3.07-21.98-5.55-24.47-2.46-2.46-3.04-5.03-2.52-8.64.1-.6.18-1.1.39-2.15.69-3.54.77-5.04.08-6.84-.91-2.38-3.31-4.41-7.79-6.26-5.08-2.09-6.52-4.84-4.89-8.44.66-1.45 1.79-3.02 3.52-5.01 1.04-1.2 5.48-5.96 5.08-5.53 6.15-6.7 8.98-11.34 8.98-16.48a15.2 15.2 0 0 1 6.5-12.89v1.26a14.17 14.17 0 0 0-5.5 11.63c0 5.47-2.93 10.29-9.24 17.16.38-.42-4.04 4.33-5.07 5.5-1.67 1.93-2.75 3.43-3.36 4.77-1.37 3.04-.23 5.22 4.36 7.1 4.71 1.95 7.32 4.16 8.34 6.83.78 2.04.7 3.67-.03 7.4-.2 1.03-.3 1.51-.38 2.09-.48 3.33.03 5.59 2.23 7.8 2.74 2.74 3.98 8.96 5.84 25.06l.24 2.03.23 2.04c.82 7.01 1.53 12.06 2.34 16.03v4.33zm0-62.16c-1.4-3.13-4.43-9.9-4.95-11.17-1.02-2.53-1.25-3.8-.91-5.18.2-.84 2.05-4.68 2.32-5.33a70.79 70.79 0 0 0 3.54-11.2v3.99a62.82 62.82 0 0 1-2.62 7.6c-.31.75-2.09 4.46-2.27 5.18-.28 1.12-.08 2.22.87 4.57.41 1.02 2.5 5.7 4.02 9.09v2.45zm0-85.09c-1.65 1.66-3.66 2.9-6.4 4.13-.25.1-13.97 5.47-20.4 8.43-9.35 4.32-16.7 5.9-23.03 5.25-5.08-.53-9.02-2.25-14.77-5.92l-3.2-2.07a77.4 77.4 0 0 0-5.44-3.27c-4.05-2.18-3.25-5.8 1.47-10.47 3.71-3.68 9.6-7.93 18.73-13.8l4.46-2.82c17.95-11.33 18.22-11.5 22.27-14.74 11.25-9 19.69-14.02 26.31-15.1v1.02c-6.37 1.1-14.62 6-25.69 14.86-4.1 3.28-4.34 3.44-22.36 14.8a652.4 652.4 0 0 0-4.45 2.83c-9.07 5.83-14.92 10.05-18.57 13.66-4.31 4.28-4.95 7.13-1.7 8.88 1.7.91 3.29 1.88 5.5 3.3l3.2 2.08c5.64 3.59 9.45 5.25 14.34 5.76 6.13.64 13.32-.9 22.52-5.15 6.46-2.98 20.18-8.35 20.4-8.44 3.04-1.37 5.1-2.71 6.81-4.69v1.47zm0-41.37v1c-6.56.26-12.11 3.13-19.71 9.08l-4.63 3.68a51.87 51.87 0 0 1-4.4 3.14c-.82.52-5.51 3.33-6.22 3.76-3.31 2-6.15 3.8-8.87 5.6a112.61 112.61 0 0 0-8.16 5.92c-4.61 3.72-7.4 6.9-7.97 9.35-.63 2.67 1.48 4.53 7.05 5.46 10.7 1.78 20.92-.05 30.45-4.65a61.96 61.96 0 0 0 17.1-12.2 41.8 41.8 0 0 0 5.36-7.42v1.92a38.94 38.94 0 0 1-4.64 6.19 62.95 62.95 0 0 1-17.39 12.41c-9.7 4.68-20.13 6.55-31.05 4.73-6.06-1-8.65-3.29-7.85-6.67.64-2.74 3.53-6.05 8.31-9.9 2.35-1.9 5.1-3.88 8.24-5.97 2.73-1.82 5.58-3.61 8.9-5.62.72-.44 5.4-3.24 6.22-3.75 1.26-.8 2.6-1.76 4.3-3.09.8-.62 3.9-3.1 4.63-3.67 7.77-6.1 13.49-9.04 20.33-9.3zm0-154.6v1c-1.75-.24-4.3.23-7.82 1.55-10.01 3.75-13.8 5.07-19.15 6.76-1.78.56-2.63.83-3.87 1.24-1.48.5-3.16.76-6.74 1.16a1550.34 1550.34 0 0 0-2.64.3c-7.8.94-11.28 2.47-11.28 6.07 0 4.45 2.89 13.18 7.96 25.81a57.34 57.34 0 0 1 2.33 7.6 258.32 258.32 0 0 1 .84 3.46c1.86 7.62 3.17 10.71 5.56 11.67 2.21.88 4.7.6 7.47-.72 3.48-1.69 7.22-4.94 11.2-9.47 1.52-1.7 2.97-3.49 4.59-5.57l3.16-4.1c2.59-3.23 6.07-12.21 8.39-20.23v3.45c-2.29 7.2-5.27 14.5-7.61 17.41-.44.55-2.67 3.46-3.15 4.09-1.63 2.1-3.1 3.9-4.62 5.62-4.08 4.61-7.9 7.94-11.53 9.7-2.99 1.44-5.77 1.75-8.28.74-2.84-1.13-4.2-4.34-6.15-12.35a2097.48 2097.48 0 0 1-.84-3.46c-.8-3.2-1.47-5.45-2.28-7.46-5.14-12.8-8.04-21.55-8.04-26.19 0-4.37 3.84-6.06 12.16-7.07a160.9 160.9 0 0 1 2.65-.3c3.5-.39 5.15-.64 6.53-1.1 1.26-.42 2.1-.7 3.88-1.26 5.34-1.68 9.11-3 19.1-6.74 3.53-1.32 6.22-1.84 8.18-1.61zM0 292c10.13-11.31 18.13-23.2 23.07-35.39 3.3-8.14 6.09-16.12 10.81-30.55l1.59-4.84c6.53-19.94 10.11-29.82 14.77-39.56 6.07-12.72 12.55-21.18 20.27-25.54 6.66-3.76 10.2-7.86 12.22-13.15a46.6 46.6 0 0 0 1.86-6.58c1.23-5.2 2.05-7.59 3.93-10.36 2.45-3.62 6.27-6.53 12.1-8.96 15.78-6.58 16.73-7.04 18.05-9.01.65-.98.83-2.15.74-4.51-.03-.73-.23-3.82-.24-4A93.8 93.8 0 0 1 119 94c0-10.04.18-11.37 2.37-13.15.52-.42 1.13-.8 2.07-1.3.27-.14 2.18-1.12 2.84-1.48a68.4 68.4 0 0 0 9.12-5.87c2.06-1.54 2.64-2.14 8.01-7.93 3.78-4.09 6.21-6.36 8.96-8.12 3.64-2.33 7.2-3.12 10.9-2.11 4.4 1.2 10.81 2 18.78 2.46 6.9.4 12.9.5 21.95.5 4.87 0 8.97.47 15.4 1.57 7.77 1.33 9.3 1.54 12.38 1.54 4.05 0 7.43-.88 10.68-2.95 5.06-3.22 8.11-4.67 11.2-5.2 3.62-.64 4.77-.46 16.55 2.06 17.26 3.7 30.85 1.36 41.06-9.7 5.1-5.53 5.48-8.9 3.48-14.8-.83-2.42-1.03-3.1-1.17-4.3-.29-2.52.5-4.71 2.71-6.93 2.65-2.65 4.72-9.17 6.22-18.29h2.03c-1.56 9.71-3.77 16.65-6.83 19.7-1.79 1.8-2.36 3.39-2.14 5.28.11 1 .3 1.63 1.07 3.9 2.22 6.53 1.76 10.66-3.9 16.8-10.77 11.66-25.07 14.13-42.95 10.3-11.42-2.45-12.55-2.62-15.78-2.06-2.77.48-5.62 1.84-10.47 4.92a20.93 20.93 0 0 1-11.76 3.27c-3.25 0-4.81-.22-12.73-1.57C212.74 59.46 208.73 59 204 59c-9.1 0-15.11-.1-22.07-.5-8.09-.47-14.62-1.29-19.2-2.54-5.62-1.53-10.17 1.38-17.85 9.66-5.5 5.94-6.08 6.53-8.28 8.18a70.38 70.38 0 0 1-9.38 6.03c-.68.37-2.58 1.35-2.84 1.49-.84.44-1.35.76-1.75 1.08C121.16 83.6 121 84.8 121 94c0 1.85.06 3.54.17 5.44 0 .17.2 3.28.24 4.03.1 2.75-.13 4.29-1.08 5.71-1.67 2.5-2.27 2.8-18.95 9.74-5.48 2.29-8.99 4.96-11.2 8.24-1.71 2.51-2.47 4.73-3.64 9.7-.83 3.5-1.21 4.92-1.94 6.83-2.18 5.73-6.05 10.19-13.1 14.18-7.3 4.12-13.55 12.28-19.46 24.66-4.6 9.64-8.17 19.46-14.67 39.32l-1.58 4.84c-4.75 14.47-7.54 22.48-10.86 30.69-5.28 13.01-13.95 25.65-24.93 37.6v-2.97zm0 78v-.5l1-.01c6.32 0 7.47 5.2 4.6 13.36a60.36 60.36 0 0 1-5.6 11.3v-1.92a57.76 57.76 0 0 0 4.65-9.72c2.69-7.6 1.71-12.02-3.65-12.02-.34 0-.67 0-1 .02v-46.59a340.96 340.96 0 0 0 13.71-8.34c13.66-9.46 29.79-37.6 29.79-53.59 0-18.1 21.57-72.64 32.23-79.42 12.71-8.09 32.24-27.96 35.8-37.75 1.93-5.3 5.5-7.27 14.42-9.37 6.15-1.44 8.64-2.42 10.67-4.79 1.5-1.74 2.72-4.79 4.33-10.3.23-.78 1.9-6.68 2.43-8.46 3.62-12.08 7.3-18.49 13.47-20.39 2.5-.76 3.03-.98 9.74-3.7 7.49-3.03 11.97-4.43 17.12-4.92 6.75-.65 13.13.75 19.55 4.67 5.43 3.32 12.19 4.72 20.17 4.56 6.03-.12 12.2-1.07 19.83-2.8 1.82-.4 7.38-1.74 8.26-1.94 2.69-.6 4.34-.89 5.48-.89 4.97 0 8.93-.05 14.2-.27 7.9-.32 15.56-.92 22.75-1.88 8.5-1.14 15.9-2.73 21.88-4.82 18.9-6.62 32.64-18.3 33.67-27.59.29-2.56.4-2.96 2.79-11.11 2.33-7.95 3.21-12.93 2.72-18.23-.2-2.24-.69-4.38-1.48-6.42-1.5-3.92-2.63-9.4-3.43-16.18h.9c.77 6.47 1.89 11.72 3.47 15.82a24.93 24.93 0 0 1 1.54 6.69c.5 5.46-.4 10.54-2.77 18.6-2.36 8.06-2.47 8.47-2.74 10.95-1.09 9.75-15.1 21.68-34.33 28.41-6.06 2.12-13.52 3.72-22.09 4.87-7.22.96-14.92 1.57-22.83 1.89-5.3.21-9.27.27-14.25.27-1.04 0-2.64.27-5.26.87-.87.2-6.43 1.53-8.26 1.94-7.68 1.73-13.92 2.7-20.03 2.82-8.15.17-15.1-1.27-20.71-4.7-6.23-3.81-12.4-5.16-18.93-4.54-5.04.48-9.44 1.86-16.84 4.86-6.75 2.74-7.29 2.95-9.82 3.73-5.73 1.76-9.28 7.96-12.81 19.72-.53 1.77-2.2 7.66-2.43 8.46-1.66 5.65-2.91 8.78-4.53 10.67-2.22 2.58-4.84 3.62-12.01 5.3-7.8 1.83-11.13 3.66-12.9 8.54-3.65 10.04-23.32 30.06-36.2 38.25C65.94 190 44.5 244.2 44.5 262c0 16.34-16.3 44.78-30.22 54.41-2.14 1.48-8.24 5.12-14.28 8.68v-1.16 46.09zm0-173.7v-1.11c7.42-3.82 14.55-10.23 21.84-18.98 3.8-4.56 14.21-18.78 15.79-20.55 1.8-2.04 4.06-3.96 7.42-6.45 1.08-.8 4.92-3.57 5.49-3.99 9.36-6.85 14-11.96 15.98-19.36.8-2.98 1.54-6.78 2.46-12.3.23-1.44 2-12.46 2.56-15.79 2.87-16.77 5.73-26.79 10.07-32.1C92.46 52.43 101.5 38.13 101.5 33c0-2.54.34-3.35 6.05-15.71.68-1.49 1.25-2.74 1.77-3.93 2.5-5.75 3.9-10.04 4.14-13.36h1c-.23 3.48-1.66 7.87-4.23 13.76-.52 1.2-1.09 2.45-1.78 3.95-5.54 12.01-5.95 12.99-5.95 15.29 0 5.47-9.09 19.84-20.11 33.31-4.2 5.12-7.03 15.06-9.86 31.64-.57 3.33-2.33 14.33-2.57 15.78-.92 5.56-1.67 9.38-2.48 12.4-2.05 7.68-6.82 12.93-16.35 19.91l-5.49 3.98c-3.3 2.45-5.51 4.34-7.27 6.31-1.53 1.73-11.94 15.93-15.76 20.53-7.52 9.02-14.88 15.6-22.61 19.46zm0 361.83v-4.33c.48 2.36 1 4.35 1.6 6.15 2 6.03 4.6 8.26 8.19 6.59C28.76 557.69 43.5 542.4 43.5 527c0-16.2 6.37-31.99 17.1-46.3 1.88-2.5 3.66-4.4 5.53-6 .73-.62 1.45-1.18 2.3-1.8l2-1.43c3.68-2.68 5.32-5.28 7.08-12.59.75-3.07 1.38-5.02 4.2-13.26l.63-1.88c3.24-9.58 4.56-14.97 4.17-18.65-.48-4.43-3.8-5.23-11.3-1.64a81.12 81.12 0 0 1-9.15 3.7c-13.89 4.67-26.96 5.8-42.66 5.42l-1.95-.05-1.45-.02a39.8 39.8 0 0 0-15.05 2.96A21.81 21.81 0 0 0 0 438.37v-1.26a23.55 23.55 0 0 1 4.55-2.57 40.77 40.77 0 0 1 16.92-3.02l1.95.05c15.6.38 28.57-.75 42.32-5.37a80.12 80.12 0 0 0 9.04-3.65c8.04-3.84 12.16-2.85 12.72 2.43.42 3.89-.92 9.34-4.21 19.08l-.64 1.88c-2.8 8.2-3.43 10.15-4.16 13.18-1.82 7.52-3.59 10.34-7.47 13.16l-2 1.43c-.84.6-1.54 1.15-2.25 1.75a35.45 35.45 0 0 0-5.37 5.84c-10.61 14.15-16.9 29.74-16.9 45.7 0 15.88-15 31.45-34.29 40.45-4.3 2.01-7.39-.66-9.56-7.18-.23-.68-.44-1.39-.65-2.13zm0-62.16v-2.45l1.46 3.27c2.1 4.8 3.46 10.33 4.26 16.77.66 5.3.84 9.3 1.04 18.5.2 9.32.5 12.75 1.63 15.05 1.28 2.6 3.67 2.35 8.29-1.5 17.14-14.3 21.82-22.9 21.82-38.62 0-7.17 1.1-12.39 3.7-17.68 2.27-4.67 3.65-6.62 13.4-19.62a69.8 69.8 0 0 1 7.6-8.79 44.76 44.76 0 0 1 3.54-3.06c.38-.3.64-.52.89-.74a10.47 10.47 0 0 0 2.63-3.32 35.78 35.78 0 0 0 2.26-5.94l.37-1.2.36-1.15c.29-.91.48-1.55.66-2.16.45-1.53.74-2.68.91-3.66.38-2.2.12-3.49-.85-4.15-2.35-1.61-9.28-.24-23.8 4.94-9.54 3.4-16.12 4.17-27.85 4.26-7.71.06-10.43.4-13.25 2.12-3.48 2.12-5.84 6.4-7.58 14.26-.5 2.2-.99 4.19-1.49 5.98v-3.98l.51-2.22c1.8-8.1 4.28-12.6 8.04-14.9 3.04-1.85 5.86-2.2 13.77-2.26 11.61-.09 18.1-.84 27.51-4.2 14.93-5.32 21.95-6.71 24.7-4.83 1.38.94 1.71 2.6 1.28 5.15a33.69 33.69 0 0 1-.94 3.78l-.66 2.17-.36 1.15-.37 1.2a36.64 36.64 0 0 1-2.33 6.1c-.8 1.53-1.61 2.52-2.86 3.61l-.92.77-1.02.83c-.9.74-1.65 1.4-2.47 2.18a68.84 68.84 0 0 0-7.48 8.66c-9.7 12.93-11.07 14.87-13.31 19.46-2.52 5.15-3.59 10.22-3.59 17.24 0 16.04-4.82 24.91-22.18 39.38-5.04 4.2-8.18 4.55-9.83 1.18-1.22-2.5-1.52-5.94-1.73-15.47-.2-9.16-.38-13.15-1.03-18.4-.79-6.34-2.12-11.8-4.19-16.49L0 495.98zM379.27 0h1.04l1.5 5.26c3.28 11.56 4.89 19.33 5.26 27.8.49 11.01-1.52 21.26-6.63 31.17-7.8 15.13-20.47 26.5-36.22 34.1-12.38 5.96-26.12 9.17-36.22 9.17-6.84 0-17.24 1.38-37.27 4.62l-2.27.37c-24.5 3.99-31.65 5-37.46 5-3.49 0-4.08-.08-19.54-2.8-3.56-.64-6.32-1.1-9-1.5-20.23-2.96-31-1.2-31.96 7.86-.1.85-.18 1.72-.29 2.81l-.27 2.73c-1.1 10.9-2.02 15.73-4.31 19.96-2.9 5.34-7.77 7.95-15.63 7.95-10.2 0-12.92.6-15.5 3.17.52-.51-5.03 5.85-8.16 8.7-2.75 2.5-14.32 12.55-15.77 13.83a341.27 341.27 0 0 0-6.54 5.92c-6.97 6.49-11.81 11.76-14.6 16.15-5.92 9.3-10.48 18.04-11.69 24.08-1.66 8.3 3.67 9.54 19.02 1.21a626.23 626.23 0 0 1 44.54-21.9c3.5-1.56 14.04-6.2 15.68-6.95 5.05-2.25 8.3-3.8 10.78-5.15l1.95-1.07 2.18-1.18c1.76-.94 3.38-1.76 5-2.55 18.1-8.72 34.48-10.46 50.33-1.2 22.89 13.34 38.28 37.02 38.28 56.44 0 19.12-.73 25.13-5.18 33.2a45.32 45.32 0 0 1-4.94 7.12c-6.47 7.77-11.81 16.2-12.76 21.27-1.2 6.34 4.69 7.03 20.17-.05 13.31-6.08 22.4-14.95 28.5-26.32a80.51 80.51 0 0 0 6.1-15.13c.9-2.98 3.17-11.65 3.41-12.48a29.02 29.02 0 0 1 1.75-4.83c7.47-14.93 21.09-30.5 36.25-37.24 7.61-3.38 13-9.65 19.4-20.79.84-1.48 4.26-7.64 5.14-9.17 3.52-6.1 6.22-9.7 9.37-11.98 10.15-7.4 28.7-11.1 50.29-11.1 7.52 0 16.54-1.24 27.51-3.58a420.1 420.1 0 0 0 14.96-3.52c-1.3.33 15.54-3.98 19.42-4.89 14.15-3.33 41.07-5.01 64.11-5.01 17.36 0 27.82-9.23 38.53-38.67 6.62-18.21 6.62-26.37 2.69-34.35l-1.18-2.37A13.36 13.36 0 0 1 587.5 58c0-4.03 0-4.01 2.5-24.56.46-3.73.8-6.74 1.12-9.64.9-8.45 1.38-15.2 1.38-20.8 0-.94-.02-1.94-.04-3h1c.03 1.06.04 2.06.04 3 0 5.65-.48 12.43-1.39 20.9-.3 2.91-.66 5.93-1.11 9.66-2.5 20.45-2.5 20.47-2.5 24.44 0 1.97.45 3.57 1.45 5.68.24.51 1.16 2.35 1.17 2.36 4.06 8.24 4.06 16.68-2.65 35.13-10.84 29.8-21.63 39.33-39.47 39.33-22.96 0-49.83 1.68-63.89 4.99-3.86.9-20.69 5.2-19.4 4.88a421.05 421.05 0 0 1-14.99 3.53c-11.04 2.35-20.11 3.6-27.72 3.6-21.4 0-39.76 3.67-49.7 10.9-3 2.19-5.64 5.7-9.1 11.68-.87 1.52-4.29 7.68-5.14 9.17-6.49 11.3-12 17.71-19.86 21.2-14.9 6.63-28.38 22.03-35.75 36.77a28.17 28.17 0 0 0-1.69 4.67c-.23.8-2.5 9.49-3.4 12.5a81.48 81.48 0 0 1-6.19 15.3c-6.2 11.56-15.44 20.58-28.96 26.76-16.1 7.36-23 6.55-21.58-1.04 1-5.29 6.4-13.83 12.99-21.73a44.33 44.33 0 0 0 4.82-6.96c4.35-7.88 5.06-13.77 5.06-32.72 0-19.04-15.19-42.4-37.72-55.55-15.57-9.08-31.62-7.38-49.45 1.21a132.9 132.9 0 0 0-7.14 3.71l-1.95 1.07a158.83 158.83 0 0 1-10.85 5.19c-1.65.74-12.18 5.38-15.69 6.95a625.25 625.25 0 0 0-44.46 21.86c-15.95 8.66-22.37 7.16-20.48-2.29 1.24-6.2 5.83-15.02 11.82-24.42 2.85-4.48 7.74-9.8 14.77-16.34 1.98-1.85 4.12-3.79 6.56-5.94 1.46-1.29 13.02-11.33 15.75-13.82 3.09-2.8 8.6-9.14 8.14-8.67 2.82-2.82 5.75-3.46 16.2-3.46 7.5 0 12.04-2.43 14.75-7.42 2.2-4.07 3.11-8.84 4.2-19.59l.26-2.73.3-2.81c.56-5.42 4.47-8.5 11.23-9.6 5.44-.88 12.51-.51 21.86.86 2.7.4 5.47.86 9.04 1.49 15.33 2.7 15.96 2.8 19.36 2.8 5.73 0 12.9-1.03 37.3-5l2.27-.36c20.1-3.26 30.52-4.64 37.43-4.64 9.95 0 23.54-3.18 35.78-9.08 15.57-7.5 28.09-18.73 35.78-33.65 5.02-9.75 7-19.82 6.51-30.67-.37-8.37-1.96-16.08-5.23-27.57L379.27 0zm13.68 0h1.02c.78 3.9 1.92 8.7 3.51 14.88 3.63 14.05 3.06 27.03-.75 38.77a61 61 0 0 1-11.35 20.68 138.36 138.36 0 0 1-19.32 18.77c-11.32 9.02-23.36 15.49-35.95 18.39a258.63 258.63 0 0 1-22.57 4.07c-3.17.44-6.36.85-10.3 1.32l-9.39 1.12c-11.53 1.41-17.45 2.55-21.64 4.46-9.28 4.21-28.35 6.04-49.21 6.04-1.37 0-2.8-.12-4.3-.35-2.62-.41-5-1.03-9.14-2.29-7.34-2.21-9.63-2.75-12.63-2.56-3.9.23-6.63 2.29-8.47 6.89-1.86 4.66-2.42 7.53-3.34 14.98-1.1 8.98-2.87 12.12-9.97 14.3a40.12 40.12 0 0 0-6.8 2.66c-.63.33-1.16.64-1.76 1.02l-1.34.86c-1.9 1.14-3.86 1.49-9.25 1.49-3.2 0-8.83-.55-9.51-.39-1.22.28-.75-.14-7.14 6.24-1.5 1.5-3.49 3.18-6.32 5.37-1.52 1.18-7.16 5.43-7.94 6.03-4.96 3.78-8.33 6.6-11.06 9.38-4.88 4.98-6.85 9.15-5.56 12.7 1.34 3.67 4.07 4.42 8.9 2.82a55.72 55.72 0 0 0 7.77-3.48c1.5-.77 7.78-4.13 9.37-4.96a116.8 116.8 0 0 1 12.31-5.68 162.2 162.2 0 0 0 11.04-4.84c2.04-.97 10.74-5.16 13-6.22 4.41-2.1 8.1-3.78 11.65-5.29 17.14-7.3 29.32-9.9 37.67-6.65l5.43 2.1c2.3.88 4.17 1.62 6.02 2.38a150.9 150.9 0 0 1 13.07 6c18.34 9.63 30.35 22.13 34.79 39.87 6.96 27.85 3.6 45.53-8.08 62.4-3.97 5.75-3.52 9.2.06 8.97 4.14-.28 10.21-4.95 15.11-12.52 3.1-4.8 5.1-10.45 8.05-21.53l1.69-6.35c.66-2.47 1.24-4.52 1.83-6.5 4.93-16.56 11-27.28 21.56-34.76 7.15-5.06 23.73-15.5 25.48-16.75 6.74-4.81 10.53-9.44 14.34-18 7.74-17.44 21.09-24.34 44.47-24.34 9.36 0 17.91-1.13 29.53-3.49a624.86 624.86 0 0 0 6.2-1.28c2.4-.5 4.07-.84 5.66-1.13 4.03-.74 7.04-1.1 9.61-1.1 4.44 0 9.39-1 31.39-5.99l2.95-.66c16.34-3.67 25.64-5.35 31.66-5.35 1.54 0 2.4.01 6.4.1 7.8.15 12.27.13 17.33-.2 16.41-1.06 26.73-5.36 29.8-14.56a87.1 87.1 0 0 1 3.55-8.83c-.15.31 2.29-4.96 2.9-6.38 5.38-12.3 5.57-21.92-1.44-39.44a86.4 86.4 0 0 1-5.26-20.72c-1.61-11.98-1.38-23.14.1-40.35l.2-2.12h1l-.2 2.2c-1.48 17.15-1.7 28.24-.11 40.14a85.4 85.4 0 0 0 5.2 20.47c7.1 17.78 6.91 27.67 1.43 40.22-.62 1.43-3.06 6.72-2.91 6.4a86.17 86.17 0 0 0-3.52 8.73c-3.23 9.72-13.9 14.15-30.68 15.24-5.1.33-9.58.35-17.42.2-3.98-.09-4.84-.1-6.37-.1-5.91 0-15.18 1.67-31.44 5.32l-2.95.67c-22.16 5.02-27.05 6.01-31.61 6.01-2.5 0-5.45.36-9.43 1.09-1.58.29-3.25.62-5.64 1.11a4894.21 4894.21 0 0 0-6.2 1.29c-11.68 2.37-20.3 3.51-29.73 3.51-23.02 0-36 6.71-43.53 23.66-3.9 8.8-7.82 13.58-14.7 18.5-1.78 1.27-18.36 11.7-25.48 16.75-10.34 7.32-16.3 17.87-21.19 34.23-.58 1.96-1.15 4-1.82 6.47l-1.69 6.35c-2.98 11.18-5 16.9-8.17 21.81-5.05 7.81-11.37 12.68-15.89 12.98-4.7.31-5.3-4.23-.94-10.53 11.52-16.64 14.82-34.03 7.92-61.6-4.35-17.42-16.16-29.72-34.27-39.22-4-2.1-8.2-4-12.99-5.97-1.84-.75-3.7-1.49-6-2.38l-5.43-2.08c-8.03-3.12-20.02-.58-36.92 6.63-3.52 1.5-7.21 3.19-11.61 5.27l-13 6.22c-4.71 2.22-8.16 3.75-11.11 4.88a115.87 115.87 0 0 0-12.21 5.63c-1.58.83-7.86 4.18-9.37 4.96a56.55 56.55 0 0 1-7.9 3.54c-5.3 1.75-8.62.85-10.17-3.43-1.46-4.02.66-8.5 5.8-13.74 2.75-2.82 6.16-5.66 11.15-9.48.79-.6 6.43-4.85 7.94-6.02a66.96 66.96 0 0 0 6.23-5.28c6.74-6.74 6.1-6.16 7.61-6.51.87-.2 6.69.36 9.74.36 5.22 0 7.03-.32 8.74-1.35l1.31-.84c.62-.4 1.18-.72 1.84-1.07a41.07 41.07 0 0 1 6.96-2.72c6.64-2.04 8.22-4.84 9.28-13.47.93-7.53 1.5-10.47 3.4-15.24 1.99-4.95 5.04-7.26 9.34-7.51 3.17-.2 5.5.35 12.97 2.6a63.54 63.54 0 0 0 9.02 2.26c1.45.22 2.83.34 4.14.34 20.71 0 39.7-1.82 48.8-5.96 4.32-1.96 10.29-3.1 21.93-4.53l9.4-1.12c3.92-.48 7.11-.88 10.27-1.32 8.16-1.14 15.4-2.43 22.49-4.06 12.42-2.86 24.33-9.26 35.55-18.2a137.4 137.4 0 0 0 19.18-18.64 60.02 60.02 0 0 0 11.15-20.32c3.76-11.57 4.32-24.36.75-38.23A284.86 284.86 0 0 1 392.95 0zM506.7 0h1.26c-.5.66-.9 1.18-1.17 1.51-3.95 4.96-6.9 7.92-9.82 9.57A10.02 10.02 0 0 1 492 12.5c-2.38 0-4.24.67-6.71 2.21l-2.65 1.71c-4.38 2.8-8.01 4.08-13.64 4.08-5.6 0-9.99-1.26-16.08-4.05a202.63 202.63 0 0 1-2.3-1.06l-2.18-.98c-1.6-.7-2.92-1.17-4.17-1.48a13.42 13.42 0 0 0-3.27-.43c-2.3 0-4.3-.68-11-3.37l-1.56-.62c-5-1.97-8.1-2.82-10.52-2.66-2.93.2-4.42 2.03-4.42 6.15 0 20.76-5.21 50.42-12.15 57.35-7.58 7.59-26.55 23.7-34.06 29.06-13.16 9.4-31.17 20.2-44.11 25.06a106.87 106.87 0 0 1-13.32 4.03c-3.28.78-6.6 1.43-11.25 2.24-.53.1-8.8 1.5-11.5 1.99-4.86.87-9.3 1.74-14 2.76-20.62 4.48-25.07 5.01-38.11 5.01-2.49 0-2.9-.07-14.05-2-2.42-.42-4.31-.73-6.15-1-8.11-1.19-13.83-1.36-17.64-.2-4.54 1.4-5.93 4.65-3.7 10.52 2.02 5.28 4.84 8.61 8.84 10.74 3.26 1.74 6.75 2.6 13.82 3.71 9.42 1.48 10.94 1.75 15.5 2.92a78.2 78.2 0 0 1 18.62 7.37c8.3 4.58 14.58 11.5 19.98 20.89 2.73 4.73 9.46 19.33 10.54 21.19 3.4 5.85 6.26 6.63 10.89 2 4.95-4.94 10.35-8.37 21.13-14.06.47-.25 2.06-1.1 2.12-1.12 7.98-4.21 11.92-6.51 15.87-9.54 5.11-3.9 8.66-8.1 10.77-13.11 8.52-20.24 20.75-33.31 32.46-33.31l5.5.03c10.53.08 17.35.02 24.9-.31 13.66-.62 23.78-2.09 29.39-4.67 5.85-2.7 13.42-5.49 24.18-9.02 3.46-1.14 6.29-2.05 12.7-4.1 7.7-2.45 11.08-3.54 15.17-4.9a1059.43 1059.43 0 0 1 11.33-3.72c3.67-1.2 5.96-2 8.03-2.78a59.88 59.88 0 0 0 6.66-2.94c1.87-.98 3.76-2.1 5.86-3.5 3.48-2.33 6.15-3.13 12.04-4.13l1.15-.2c5.71-1.01 9-2.3 12.76-5.63 7.82-6.96 8.58-23.18 3.84-44.52-1.7-7.67-2.1-19.28-1.57-35.47A837.22 837.22 0 0 1 546.76 0h1l-.15 3.06c-.32 6.42-.53 11.02-.68 15.62-.51 16.1-.12 27.65 1.56 35.21 4.82 21.68 4.04 38.2-4.16 45.48-3.91 3.48-7.37 4.84-13.24 5.87l-1.16.2c-5.76.99-8.32 1.75-11.65 3.98a63.73 63.73 0 0 1-5.96 3.56 60.86 60.86 0 0 1-6.77 2.99c-2.09.79-4.39 1.58-8.07 2.79a5398.31 5398.31 0 0 1-11.32 3.71c-4.1 1.37-7.48 2.46-15.18 4.92-6.42 2.04-9.24 2.95-12.7 4.08-10.73 3.53-18.27 6.3-24.07 8.98-5.76 2.66-15.97 4.14-29.77 4.77-7.56.33-14.4.39-24.95.31l-5.49-.03c-11.19 0-23.16 12.79-31.54 32.7-2.19 5.19-5.84 9.52-11.08 13.52-4.02 3.07-7.99 5.39-16.01 9.62l-2.12 1.12c-10.7 5.65-16.04 9.04-20.9 13.9-5.14 5.14-8.75 4.15-12.45-2.22-1.12-1.92-7.85-16.5-10.54-21.2-5.33-9.24-11.48-16.02-19.6-20.5a77.2 77.2 0 0 0-18.4-7.28c-4.5-1.17-6.02-1.43-15.4-2.9-7.17-1.12-10.74-2-14.13-3.81-4.22-2.25-7.2-5.77-9.3-11.27-2.43-6.39-.78-10.26 4.34-11.83 4-1.22 9.82-1.05 18.08.17 1.84.27 3.74.58 6.17 1 11.02 1.9 11.48 1.98 13.88 1.98 12.96 0 17.35-.52 37.9-4.99 4.71-1.02 9.16-1.9 14.03-2.77 2.71-.48 10.98-1.9 11.5-1.98 4.64-.81 7.95-1.46 11.2-2.23 4.55-1.07 8.76-2.34 13.2-4 12.83-4.81 30.79-15.59 43.88-24.94 7.47-5.33 26.4-21.4 33.94-28.94C407.3 61.98 412.5 32.49 412.5 12c0-4.61 1.86-6.9 5.35-7.15 2.63-.18 5.8.7 10.96 2.73l1.56.62c6.53 2.62 8.53 3.3 10.63 3.3 1.14 0 2.3.16 3.5.46 1.32.33 2.68.82 4.34 1.53a90.97 90.97 0 0 1 3.34 1.52l1.15.54c5.98 2.73 10.23 3.95 15.67 3.95 5.41 0 8.87-1.21 13.1-3.92.2-.13 2.1-1.38 2.66-1.72 2.62-1.63 4.64-2.36 7.24-2.36 1.47 0 2.94-.43 4.47-1.3 2.78-1.56 5.67-4.45 9.54-9.31l.7-.89zM324.54 600h-2.03c.49-2.96.91-6.2 1.28-9.66.44-4.1.76-8.25.98-12.21.08-1.39.14-2.65-.35-7.29-.47-1.94-.93-4.14-1.36-6.54-2.01-11.26-2.66-22.9-1.14-33.78a60.76 60.76 0 0 1 5.18-17.95 70.78 70.78 0 0 1 12.6-18.22c3.38-3.6 5.53-5.5 11.83-10.79 4.5-3.78 6.35-5.56 7.52-7.5.64-1.07.95-2.06.95-3.06 0-1.75 0-1.74-.75-9.23-.36-3.7-.57-6.3-.68-8.96-.5-12.1 1.62-19.6 8.11-21.76 15.9-5.3 25.89-12.1 33.45-25.54C409.6 390.65 425.85 376 436 376c12.36 0 20-1.96 29.41-8.8 6.76-4.92 9.5-6.6 12.47-7.46 2.22-.64 3.8-.74 9.12-.74 1.86 0 3.53-.83 5.57-2.62 1.08-.96 5.11-5.12 5.6-5.6 6.04-5.85 11.98-8.78 20.83-8.78 2.45 0 4.54.04 7.32.12 7.51.23 8.87.17 11.27-.7 3.03-1.1 5.53-3.03 14.75-11.17 8-7.06 10.72-8.92 22.87-16.47 1.44-.9 2.59-1.63 3.69-2.37a69.45 69.45 0 0 0 9.46-7.5c4.12-3.88 8.02-7.85 11.64-11.9v2.98a201.58 201.58 0 0 1-10.27 10.38c-3.18 3-6.2 5.35-9.72 7.7-1.12.76-2.28 1.5-3.75 2.4-12.05 7.5-14.71 9.32-22.6 16.28-9.46 8.35-12.01 10.32-15.39 11.55-2.74 1-4.19 1.06-12.01.82-2.76-.08-4.83-.12-7.26-.12-8.27 0-13.75 2.7-19.43 8.22-.44.43-4.52 4.64-5.68 5.66-2.37 2.09-4.46 3.12-6.89 3.12-5.1 0-6.6.1-8.56.66-2.67.78-5.29 2.37-11.85 7.15-9.8 7.13-17.85 9.19-30.59 9.19-9.22 0-24.96 14.2-34.13 30.49-7.84 13.94-18.24 21.02-34.55 26.46-5.31 1.77-7.21 8.51-6.75 19.78.1 2.6.31 5.19.68 8.84.75 7.62.75 7.58.75 9.43 0 1.38-.42 2.73-1.24 4.09-1.33 2.2-3.26 4.07-7.94 8-6.25 5.24-8.36 7.12-11.67 10.63a68.8 68.8 0 0 0-12.25 17.71 58.8 58.8 0 0 0-5 17.36c-1.49 10.66-.85 22.09 1.13 33.15.43 2.37.88 4.53 1.33 6.44.16.66.3 1.25.6 4.06a249.3 249.3 0 0 1-1.17 16.12c-.37 3.37-.78 6.53-1.25 9.44zm-13.4 0h-1.05l.12-.28c3.07-7.16 4.29-11.83 4.29-18.72 0-3.57-.07-4.93-.76-15.65-.77-12.04-1-19.64-.55-28.3.58-11.5 2.4-22.1 5.81-32.16 1.3-3.8 2.8-7.5 4.55-11.1 3.46-7.14 6.83-12.39 10.42-16.6a59.02 59.02 0 0 1 4.35-4.56c.43-.4 3-2.8 3.67-3.45 5.72-5.6 7.51-11.52 7.51-29.18 0-18.84 2.9-23.77 15.82-28.24 1.09-.37 1.92-.67 2.77-.98a51.3 51.3 0 0 0 6.1-2.7c4.95-2.6 9.64-6.22 14.44-11.42 25.5-27.63 37.15-35.16 56.37-35.16 8.28 0 14.54-1.95 22-6.3 1.78-1.03 13.82-8.82 18.16-11.27 2.83-1.59 5.66-3.03 8.63-4.39 7.92-3.6 13.97-4.45 26.6-4.8 7.53-.2 10.7-.49 14.26-1.58 4.55-1.4 8.06-4 10.93-8.43 2.2-3.41 6.85-7.08 14.66-12.06 1.61-1.03 3.27-2.05 5.65-3.5 9.53-5.85 11.56-7.13 14.81-9.57 5.34-4 9.3-8.37 13.68-14.77a204.2 204.2 0 0 0 5.62-8.75v1.9c-1.97 3.17-3.4 5.38-4.8 7.42-4.42 6.48-8.46 10.92-13.9 15-3.29 2.46-5.32 3.75-14.89 9.61a375.06 375.06 0 0 0-5.63 3.5c-7.7 4.9-12.26 8.52-14.36 11.76-3 4.63-6.7 7.39-11.48 8.85-3.68 1.12-6.9 1.42-14.53 1.63-12.5.34-18.44 1.18-26.2 4.7a111.08 111.08 0 0 0-8.56 4.35c-4.3 2.43-16.34 10.22-18.15 11.27-7.6 4.43-14.03 6.43-22.5 6.43-18.87 0-30.3 7.4-55.63 34.84-4.88 5.28-9.67 8.97-14.7 11.62-2 1.05-4 1.92-6.23 2.75-.86.32-1.7.62-5.37 1.87-5.08 1.76-7.44 3.25-9.28 6.37-2.23 3.78-3.29 9.94-3.29 20.05 0 17.9-1.87 24.07-7.8 29.89-.69.67-3.27 3.06-3.69 3.46a58.04 58.04 0 0 0-4.28 4.49c-3.53 4.14-6.86 9.32-10.28 16.38a95.19 95.19 0 0 0-4.5 10.99c-3.38 9.97-5.18 20.48-5.76 31.9-.44 8.6-.22 16.17.55 28.17.69 10.76.76 12.12.76 15.72 0 6.35-1.02 10.87-4.35 19zm25.08 0h-1c-.04-4.73.06-9.39.28-15.02.26-6.41-.4-11.79-2.53-24.37l-.31-1.86c-2.12-12.55-2.76-19.35-1.97-26.47 1.03-9.25 4.75-16.68 12-22.67 22.04-18.2 29.81-30.18 29.81-44.61 0-2.6-.3-4.81-.98-8.17-.97-4.79-1.1-5.68-.97-7.57.2-2.56 1.27-4.7 3.56-6.72 2.67-2.35 7.05-4.6 13.72-7.01 9.72-3.5 15.52-9.18 24.3-21.57l1.78-2.5c4.48-6.33 7.1-9.63 10.43-12.78 4.31-4.07 8.98-6.77 14.54-8.17 13.3-3.32 20.37-5.47 25.34-7.64a49.5 49.5 0 0 0 5.28-2.7c1.1-.65 1.75-1.04 4.24-2.6 2.7-1.68 5.22-2.08 11.38-2.28 5.44-.18 7.9-.43 10.97-1.41a21.47 21.47 0 0 0 9.54-6.22c4.87-5.3 10.03-7.61 17.79-8.9 1.07-.18 1.88-.3 3.86-.58 6.9-.97 9.94-1.69 13.48-3.62 4.5-2.45 6.79-4.44 23.46-19.68l3.14-2.85c9.65-8.71 16.12-13.83 21.42-16.48 4.25-2.12 7.6-4.69 11.22-8.6v1.45c-3.42 3.57-6.69 6-10.78 8.05-5.18 2.59-11.61 7.67-21.2 16.32l-3.12 2.85c-16.8 15.35-19.05 17.3-23.66 19.82-3.68 2-6.8 2.75-13.82 3.73-1.97.28-2.78.4-3.84.57-7.56 1.26-12.52 3.48-17.21 8.6a22.47 22.47 0 0 1-9.97 6.5c-3.2 1-5.72 1.27-11.25 1.45-5.98.2-8.39.57-10.89 2.13a144 144 0 0 1-4.25 2.61 50.48 50.48 0 0 1-5.39 2.75c-5.04 2.2-12.15 4.37-25.5 7.7-9.74 2.44-15.26 7.65-24.4 20.56l-1.77 2.5c-8.9 12.54-14.82 18.34-24.78 21.93-6.57 2.36-10.85 4.57-13.4 6.82-2.1 1.86-3.05 3.74-3.22 6.04-.13 1.76 0 2.63.95 7.3.7 3.42 1 5.7 1 8.37 0 14.79-7.93 27-30.18 45.39-7.03 5.8-10.64 13-11.64 22-.78 7-.14 13.73 1.96 26.2l.32 1.85c2.15 12.65 2.8 18.07 2.54 24.58-.22 5.57-.32 10.2-.28 14.98zM95.9 600h-2.04c.68-3.82 1.14-8.8 1.61-15.98.2-3.11.27-4.06.39-5.6 1.3-17.54 4.04-27.14 11.5-33.2 4.65-3.77 7.22-8.92 8.67-16 .51-2.52.7-3.87 1.33-9.17.66-5.5 1.16-8.06 2.24-10.36 1.45-3.09 3.82-4.69 7.39-4.69 14.28 0 38.48 9.12 53.6 20.2 8.66 6.35 21.26 13.32 31.74 17.11 13.03 4.71 21.89 4.41 24.75-1.73 1.7-3.64 1.92-4.11 2.65-5.77 2.93-6.67 4.69-12.2 5.25-17.5.23-2.17.24-4.23.02-6.2-.32-2.75-1.42-4.55-4.08-7.35l-1.32-1.37a30.59 30.59 0 0 1-2.41-2.79 30.37 30.37 0 0 1-2.5-4.07l-1.13-2.14c-1.62-3.1-2.68-4.6-4.12-5.56-5.26-3.5-14.8-5.5-28.55-6.83a272.42 272.42 0 0 0-9.04-.71l-2.18-.17c-9.57-.73-15.12-1.56-19.06-3.2C156.57 471.07 136 450.5 136 440c0-5.34 1.74-9.53 5.47-14.13 1.98-2.44 11.12-11.71 12.79-13.54 4.52-4.97 10.16-9.54 17.68-14.66 2.8-1.9 14.78-9.6 17.49-11.49a50.54 50.54 0 0 0 6.34-5.43c1.53-1.5 6.96-7.13 7.12-7.3 7.18-7.3 12.7-11.56 19.74-14.38 3.36-1.34 8.13-2.79 17.45-5.38a9577.18 9577.18 0 0 1 11.78-3.28 602.6 602.6 0 0 0 12.67-3.7c20.4-6.24 34-12.08 40.79-18.44 8.74-8.2 11.78-13.84 15.73-26.02 2.02-6.22 3.09-9.04 5.07-12.72 9.54-17.71 28.71-39.37 43.5-45.45C383.77 238.25 389 232.34 389 226c0-2.89 2.73-8.4 6.83-13.73 4.76-6.2 10.65-11.36 16.75-14.18 12.5-5.77 33.5-10.09 47.42-10.09 5.32 0 9.83-1.5 16.42-4.89 9.2-4.71 10.1-5.11 13.58-5.11 10.42 0 32.06-2.55 45.76-5.97l3.88-.98 3.47-.89c2.6-.66 4.33-1.08 5.93-1.43 3.9-.86 6.76-1.23 9.58-1.17 2.74.06 5.47.52 8.67 1.48 4.56 1.37 13.71-.9 22.87-5.68a68.07 68.07 0 0 0 9.84-6.2v2.4c-11.09 8.14-25.76 13.66-33.29 11.4a29.72 29.72 0 0 0-8.13-1.4c-2.63-.05-5.36.3-9.11 1.12a238 238 0 0 0-9.33 2.3l-3.9.99C522.38 177.43 500.58 180 490 180c-2.99 0-3.91.4-12.67 4.89-6.85 3.51-11.61 5.11-17.33 5.11-13.65 0-34.35 4.26-46.58 9.9-5.78 2.67-11.42 7.62-16 13.58-3.85 5.02-6.42 10.2-6.42 12.52 0 7.27-5.8 13.82-20.62 19.92-14.27 5.88-33.16 27.21-42.5 44.55-1.9 3.55-2.95 6.28-4.93 12.4-4.05 12.47-7.23 18.39-16.27 26.86-7.08 6.64-20.87 12.57-41.57 18.89a604.52 604.52 0 0 1-12.7 3.71 1495.1 1495.1 0 0 1-11.8 3.28c-9.24 2.58-13.97 4.01-17.24 5.32-6.73 2.69-12.05 6.8-19.05 13.92-.15.15-5.6 5.8-7.15 7.32a52.4 52.4 0 0 1-6.6 5.65c-2.74 1.92-14.75 9.63-17.5 11.5-7.4 5.04-12.94 9.52-17.33 14.35-1.72 1.9-10.8 11.11-12.71 13.46-3.47 4.26-5.03 8.03-5.03 12.87 0 9.5 20 29.5 33.38 35.08 3.67 1.53 9.1 2.34 18.45 3.05a586.23 586.23 0 0 0 4.34.32c3.24.23 5.07.37 6.93.55 14.08 1.37 23.82 3.4 29.45 7.17 1.82 1.2 3.02 2.91 4.8 6.29l1.11 2.13a28.55 28.55 0 0 0 2.34 3.81c.62.83 1.3 1.6 2.26 2.61.23.24 1.1 1.16 1.32 1.37 2.93 3.09 4.24 5.23 4.61 8.5.24 2.12.23 4.33-.01 6.64-.59 5.55-2.4 11.25-5.41 18.1-.74 1.67-.96 2.15-2.66 5.8-3.49 7.47-13.33 7.8-27.25 2.77-10.67-3.86-23.43-10.92-32.25-17.38C164.62 515.96 140.82 507 127 507c-5 0-6.4 3.02-7.64 13.29a99.03 99.03 0 0 1-1.36 9.33c-1.53 7.5-4.3 13.04-9.37 17.16-6.87 5.58-9.5 14.78-10.77 31.8-.11 1.52-.18 2.47-.38 5.57-.46 7.01-.91 11.99-1.57 15.85zm8.05 0h-1.02c.29-1.41.58-2.94.9-4.59l1.05-5.62c2.5-13.3 4.2-19.92 6.68-24.05 1.7-2.84 3.68-5.5 8.05-11.03 8.21-10.36 10.88-14.55 10.88-18.71l-.02-1.69c-.02-1.78-.02-2.7.02-3.77.21-5.05 1.47-8.2 4.64-9.4 3.92-1.5 10.39.44 20.12 6.43 9.56 5.88 17.53 10.7 25.91 15.66 1.31.78 14.27 8.41 17.67 10.45a714.21 714.21 0 0 1 6.42 3.9c13.82 8.5 38.94 5.05 46.3-7.83 3.6-6.28 4.54-8.52 7.78-17.32a82.3 82.3 0 0 1 1.18-3.07 42.27 42.27 0 0 1 4.06-7.64c9.33-13.98 14.92-26.1 14.92-36.72 0-3.66.75-6.62 3.36-14.85.52-1.64.83-2.66 1.15-3.73 3.64-12.23 3.04-19.12-4.29-24a23.1 23.1 0 0 0-9.98-3.78c-7.2-.93-14.49 1.17-23.91 5.88-1.55.78-6.64 3.44-7.6 3.93a62.6 62.6 0 0 0-4.14 2.3l-4.4 2.66c-11.62 6.92-20.4 9.18-32.81 6.08-3.32-.84-6.24-1.4-13.1-2.64-13.25-2.39-18.7-3.75-23.33-6.46-6.23-3.67-7.46-9.02-2.88-16.65A93.1 93.1 0 0 1 172 415.42a157 157 0 0 1 8.32-7.66c-.07.05 6.16-5.3 7.82-6.77a85.12 85.12 0 0 0 6.5-6.33c7.7-8.46 12.78-13.36 20.08-18.57 9.94-7.1 21.4-12.36 35.18-15.58 37.03-8.64 51-12.7 58.83-17.93 8.6-5.73 21.3-24.77 36.84-54.81 5.22-10.1 12.27-18.4 21.13-25.71 5.13-4.24 9.56-7.25 17.55-12.23 7.42-4.62 9.62-6.14 11.38-8.16a21.15 21.15 0 0 0 2.95-4.87c.61-1.3 2.87-6.47 3-6.77 1.36-3 2.56-5.4 3.95-7.73 6.53-10.97 16.03-18 31.4-20.8 12.73-2.3 19.85-2.7 29.68-2.3 3.25.13 4.13.16 5.6.14 5.15-.07 9.71-1.04 16.61-3.8 20.74-8.3 38.75-12.04 59.19-12.04 3.05 0 6.03.15 10.48.48l2.09.16c12.45.96 18.08.96 25.34-.63a49.65 49.65 0 0 0 14.09-5.45v1.15a50.52 50.52 0 0 1-13.88 5.28c-7.38 1.61-13.08 1.61-25.63.65l-2.08-.16c-4.43-.33-7.39-.48-10.41-.48-20.3 0-38.2 3.72-58.81 11.96-7.01 2.8-11.7 3.8-16.97 3.88-1.5.02-2.39-.01-5.66-.14-9.76-.4-16.8-.01-29.47 2.3-15.06 2.73-24.32 9.58-30.71 20.31a72.8 72.8 0 0 0-3.9 7.63c-.12.28-2.39 5.47-3.01 6.79a22 22 0 0 1-3.1 5.1c-1.86 2.13-4.07 3.66-11.6 8.35-7.95 4.96-12.35 7.95-17.44 12.15-8.76 7.23-15.73 15.43-20.89 25.4-15.61 30.2-28.36 49.32-37.16 55.19-7.98 5.32-21.97 9.39-59.17 18.07-13.65 3.18-24.98 8.39-34.82 15.42-7.22 5.16-12.27 10.01-19.92 18.43a86.07 86.07 0 0 1-6.57 6.4c-1.67 1.48-7.91 6.83-7.84 6.77-3.27 2.84-5.8 5.16-8.26 7.62a92.1 92.1 0 0 0-14.27 18.13c-4.3 7.16-3.22 11.89 2.53 15.26 4.47 2.63 9.88 3.99 23.24 6.39a185.7 185.7 0 0 1 12.92 2.6c12.11 3.03 20.64.84 32.06-5.96l4.4-2.65c1.66-1 2.96-1.73 4.2-2.35.95-.48 6.04-3.14 7.6-3.92 9.59-4.8 17.04-6.94 24.49-5.98a24.1 24.1 0 0 1 10.4 3.93c7.82 5.21 8.45 12.52 4.7 25.13-.32 1.07-.64 2.1-1.16 3.74-2.57 8.12-3.31 11.04-3.31 14.55 0 10.88-5.66 23.14-15.08 37.28a41.28 41.28 0 0 0-3.97 7.46c-.37.9-.73 1.82-1.18 3.04-3.25 8.85-4.21 11.13-7.84 17.47-7.67 13.42-33.43 16.95-47.7 8.18a578.4 578.4 0 0 0-6.4-3.89c-3.4-2.04-16.36-9.67-17.67-10.45-8.38-4.97-16.36-9.78-25.92-15.66-9.5-5.85-15.7-7.7-19.24-6.36-2.68 1.02-3.8 3.82-4 8.51a61.12 61.12 0 0 0-.02 3.72l.02 1.7c0 4.5-2.69 8.73-11.52 19.87-3.92 4.95-5.87 7.59-7.55 10.39-2.39 3.97-4.08 10.56-6.56 23.72l-1.05 5.62-.86 4.4zm10.5 0h-1c.03-.34.04-.68.04-1 0-12.39 8.48-33.57 19.16-43.37a26.18 26.18 0 0 0 3.67-4.17 35.8 35.8 0 0 0 2.88-4.9c.36-.72 1.75-3.66 2.1-4.36 3.22-6.29 6.84-6.54 16.97.39 1.34.9 6.07 4.16 6.4 4.38 2.62 1.8 4.67 3.2 6.7 4.56 5.03 3.39 9.37 6.2 13.51 8.7 14.33 8.67 25.49 13.27 34.11 13.27 16.86 0 32.71-5.95 39.6-14.8 1.59-2.04 3.2-5.17 5.06-9.63.8-1.92 1.64-4.06 2.67-6.8l2.74-7.33c4.66-12.44 7.76-19.06 11.56-23.27 7.9-8.79 14.87-36 14.87-52.67 0-1.9.17-3.11 1.02-8.27.37-2.2.58-3.6.74-5.07.63-5.51.21-9.46-1.68-12.39-4.6-7.1-19.7-9.23-38.46-4.78a100.57 100.57 0 0 0-18.94 6.3c-5.17 2.37-17.11 9.74-16.5 9.4-6.72 3.64-12.97 4.15-24.8 1.3-29.55-7.14-30.43-8.62-15.26-26.81 17.44-20.93 47.12-46.18 56.38-46.18 9.92 0 53.84-11.98 65.78-17.95 9.46-4.73 24.32-21.18 36.82-37.85.71-.95 13.5-21.6 19.2-29.6 9.35-13.13 18.22-22.55 26.95-27.53 7.29-4.17 13.16-10.28 18.8-18.73 1.93-2.9 10.52-17.65 12.73-20.41 1.54-1.93 3-3.21 4.52-3.89 14.07-6.25 24.22-9.04 39.2-9.04h29c4.05 0 7.36-.4 22.93-2.5l4.3-.57c9.92-1.3 16.57-1.93 21.77-1.93 1.66 0 2.95.01 6.03.04 18.61.19 28.55-.48 44.86-4.03 3.1-.67 6.13-1.78 9.11-3.31v1.12a37.96 37.96 0 0 1-8.9 3.17c-16.4 3.56-26.4 4.24-45.08 4.05-3.08-.03-4.36-.04-6.02-.04-5.15 0-11.76.63-21.64 1.92l-4.3.58c-15.64 2.11-18.94 2.5-23.06 2.5h-29c-14.81 0-24.84 2.75-38.8 8.96-1.34.6-2.69 1.78-4.14 3.6-2.16 2.68-10.72 17.39-12.68 20.33-5.72 8.57-11.7 14.8-19.13 19.04-8.57 4.9-17.36 14.23-26.63 27.24-5.68 7.97-18.47 28.64-19.22 29.63-12.6 16.8-27.52 33.32-37.18 38.15-12.06 6.03-56.14 18.05-66.22 18.05-8.82 0-38.39 25.15-55.62 45.82-14.6 17.52-14.19 18.21 14.74 25.2 11.6 2.8 17.6 2.3 24.09-1.2-.67.35 11.31-7.03 16.56-9.44 5.41-2.48 11.6-4.59 19.11-6.37 19.13-4.53 34.65-2.35 39.54 5.22 2.05 3.17 2.48 7.32 1.84 13.04a96.34 96.34 0 0 1-.75 5.13c-.84 5.08-1.01 6.29-1.01 8.1 0 16.9-7.03 44.33-15.13 53.33-3.68 4.09-6.76 10.65-11.37 22.96-.35.93-2.2 5.94-2.73 7.33-1.04 2.76-1.88 4.9-2.68 6.84-1.9 4.53-3.55 7.73-5.2 9.85-7.1 9.13-23.25 15.19-40.39 15.19-8.86 0-20.15-4.65-34.63-13.42-4.15-2.51-8.5-5.32-13.55-8.72a861.54 861.54 0 0 1-6.71-4.56l-6.4-4.39c-9.68-6.63-12.61-6.42-15.5-.75-.35.68-1.74 3.62-2.1 4.35a36.77 36.77 0 0 1-2.96 5.03c-1.12 1.57-2.37 3-3.81 4.33-10.47 9.6-18.84 30.51-18.84 42.63l-.03 1zm-29.65 0h-1.1c1.17-2.52 1.79-5.2 1.79-8 0-20 4.83-42.04 12.15-49.35 5.17-5.18 7.77-8.38 9.9-12.74 2.64-5.41 3.95-12 3.95-20.91 0-6.82 1.14-11.59 3.37-15.07 1.74-2.7 3.6-4.21 8.91-7.52a31.64 31.64 0 0 0 3.9-2.79c4.61-3.96 6.58-6.2 7.72-9.41 1.43-4.02.93-9.04-1.86-16.02a68.98 68.98 0 0 0-3.99-8.07l-.93-1.7a75.47 75.47 0 0 1-2.64-5c-5.16-10.71-3.77-18.9 7.68-29.78a204 204 0 0 1 26.81-21.55c3.96-2.69 16.8-10.8 19.24-12.5 1.99-1.4 4.33-3.3 7.77-6.3-.02 0 7.23-6.39 9.47-8.3 4.97-4.26 9.09-7.5 13.05-10.15 4.72-3.15 8.97-5.28 12.87-6.32 12.78-3.41 15.6-4.18 21.77-5.97 12.55-3.64 21.96-6.9 28.14-10a45.47 45.47 0 0 1 7.47-2.79c8.66-2.66 12.02-4.1 16.97-8.1 6.78-5.46 13.07-14.25 19.33-27.87 15.97-34.77 19.08-39.39 32.15-49.19 3.14-2.36 6.37-4.1 11.43-6.4l2.33-1.04c11.93-5.35 16.87-8.93 21.1-17.38 1.88-3.77 2.48-6.29 3.37-12.27.78-5.19 1.48-7.56 3.53-10.25 2.57-3.4 7.03-6.27 14.36-9.01 3.37-1.26 7.36-2.5 12.05-3.73 16.33-4.3 25.28-5.36 39.6-5.81 6.9-.22 9.5-.56 12.66-2 1.19-.54 2.36-1.23 3.58-2.11 3.7-2.7 8.14-4.54 13.24-5.67 5.71-1.27 10.69-1.54 18.7-1.45l2.35.02c2.82 0 6.8-1 19.7-4.69 10.83-3.08 15.95-4.31 19.3-4.31.82 0 1.9.13 3.55.41l5.01.9c9.82 1.68 17.44 1.89 25.15-.21 7.98-2.18 14.8-6.77 20.29-14.24V147c-5.47 7.04-12.21 11.42-20.03 13.55-7.88 2.15-15.63 1.94-25.58.23l-5-.9c-1.6-.26-2.64-.39-3.39-.39-3.2 0-8.32 1.22-19.74 4.48-12.35 3.53-16.3 4.52-19.26 4.52l-2.36-.02c-7.94-.1-12.85.17-18.47 1.42-4.97 1.11-9.3 2.9-12.88 5.5a21.4 21.4 0 0 1-3.75 2.22c-3.32 1.5-6 1.87-13.04 2.09-14.25.44-23.13 1.5-39.37 5.77a125.56 125.56 0 0 0-11.95 3.7c-7.17 2.7-11.49 5.46-13.93 8.68-1.9 2.52-2.58 4.76-3.33 9.8-.9 6.08-1.53 8.68-3.47 12.56a30.6 30.6 0 0 1-9.66 11.45c-3.12 2.26-5.95 3.73-11.93 6.4l-2.31 1.04c-5.01 2.27-8.18 3.99-11.25 6.29-12.9 9.68-15.93 14.17-31.85 48.8-6.31 13.76-12.7 22.68-19.6 28.25-5.08 4.1-8.53 5.57-17.3 8.27a44.64 44.64 0 0 0-7.33 2.73c-6.24 3.12-15.7 6.4-28.3 10.06a867.4 867.4 0 0 1-21.8 5.97c-3.77 1.01-7.93 3.1-12.56 6.19a137.35 137.35 0 0 0-12.95 10.07c-2.24 1.92-9.48 8.3-9.48 8.3a98.2 98.2 0 0 1-7.84 6.37c-2.46 1.72-15.32 9.83-19.26 12.5a203 203 0 0 0-26.69 21.45c-11.13 10.58-12.43 18.3-7.47 28.63a74.52 74.52 0 0 0 2.62 4.95l.94 1.7a69.84 69.84 0 0 1 4.03 8.17c2.88 7.2 3.4 12.46 1.89 16.73-1.22 3.43-3.28 5.77-8.02 9.84-1.14.97-2.32 1.8-5.3 3.67-3.92 2.45-5.69 3.89-7.31 6.42-2.13 3.3-3.22 7.89-3.22 14.53 0 9.05-1.34 15.79-4.05 21.34-2.19 4.49-4.85 7.77-10.1 13.01-7.07 7.07-11.85 28.9-11.85 48.65 0 2.8-.58 5.48-1.7 8zm282.54 0h-1.01l-1.1-5.8c-3.08-16.26-4.05-26.2-2.74-37.26.7-5.8.77-9.68.55-15.3-.18-4.45-.17-5.68.19-7.63.78-4.3 3.44-8.53 10.39-16.34 9.07-10.2 12.26-15.41 19.8-30.15 1.35-2.64 2.33-4.47 3.38-6.3.9-1.58 1.82-3.06 2.77-4.5 3.14-4.7 7.03-8.42 16.84-16.81 11.22-9.6 15.5-13.86 18.13-19.13.7-1.4 1.3-2.8 1.93-4.4a206 206 0 0 0 1.49-4.05c3.63-9.94 8.01-13.93 22.9-17.81 4.99-1.3 20.55-5.13 21.38-5.34 16.19-4.1 25.33-7.36 33.48-12.6 5.86-3.77 5.84-3.76 27.66-16.53l2.6-1.52c10.23-6 17.1-10.2 22.73-13.95a149.3 149.3 0 0 0 8.8-6.3 723.7 723.7 0 0 0 6.37-5.08A87.74 87.74 0 0 1 600 342.95v1.12a85.76 85.76 0 0 0-15.49 9.9c.18-.14-4.76 3.84-6.38 5.1a150.3 150.3 0 0 1-8.85 6.35c-5.65 3.76-12.53 7.96-22.78 13.97l-2.6 1.53c-21.8 12.75-21.78 12.74-27.63 16.5-8.27 5.32-17.49 8.61-33.78 12.73-.83.21-16.39 4.04-21.36 5.33-8.03 2.1-13.15 4.5-16.45 7.5-2.66 2.42-4 4.86-5.77 9.7l-1.5 4.07a51.12 51.12 0 0 1-1.96 4.47c-2.72 5.45-7.04 9.75-18.38 19.45-9.73 8.32-13.6 12.02-16.65 16.6a77.18 77.18 0 0 0-2.74 4.45c-1.05 1.81-2.01 3.63-3.35 6.25-7.58 14.81-10.82 20.08-19.96 30.36-6.83 7.7-9.4 11.78-10.15 15.86-.34 1.85-.34 3.04-.17 7.4.22 5.68.14 9.6-.55 15.47-1.3 10.92-.34 20.79 2.73 36.95l1.12 5.99zm-76.59 0h-2.1l1.39-4.3c1.04-3.3 1.93-6.78 2.68-10.4 2.65-12.73 3.27-23.63 3.27-41.3 0-5.71-1.86-9.75-4.13-9.75-2.94 0-6.96 5.61-10.93 17.08C271.14 579.68 258.3 593 238 593c-22.42 0-29.26-1.35-48.42-10.09a87.69 87.69 0 0 1-9.42-5.04c-2.95-1.8-12.78-8.57-14.84-9.72-4.2-2.36-7-2.71-9.72-.99-.63.4-1.26.91-1.9 1.55a57.69 57.69 0 0 1-4.31 3.86 147.88 147.88 0 0 1-3.06 2.44l-1 .8C137.01 582.43 134 587.18 134 597c0 1.02-.02 2.01-.07 3h-2c.05-.99.07-1.98.07-3 0-10.52 3.33-15.78 12.09-22.76a265.61 265.61 0 0 1 2-1.6c.83-.64 1.43-1.13 2.03-1.61a55.76 55.76 0 0 0 4.17-3.74c.74-.73 1.48-1.34 2.24-1.82 3.47-2.2 7-1.75 11.77.93 2.15 1.21 12.03 8 14.9 9.76a85.7 85.7 0 0 0 9.22 4.93C209.29 589.7 215.85 591 238 591c19.25 0 31.49-12.7 41.06-40.33 4.24-12.25 8.66-18.42 12.81-18.42 3.8 0 6.13 5.06 6.13 11.75 0 17.8-.63 28.8-3.3 41.7-.77 3.7-1.68 7.23-2.75 10.6-.4 1.3-.8 2.53-1.19 3.7zm-149.25 0l.5-.94a160.1 160.1 0 0 0 6.53-13.26c2.73-6.29 5.78-9.64 9.24-10.52 3.74-.95 7.15.74 12.56 5.13 5.43 4.4 6.07 4.86 7.73 5.1 1.6.22 4.28 1.14 8.86 2.95 1.3.5 10.78 4.35 13.85 5.55 3.07 1.2 5.85 2.25 8.49 3.18 3.1 1.1 5.98 2.04 8.65 2.81h-3.45c-1.76-.56-3.6-1.18-5.54-1.87a281.2 281.2 0 0 1-8.51-3.19c-3.08-1.2-12.57-5.04-13.86-5.55-4.5-1.78-7.15-2.68-8.63-2.9-1.94-.27-2.53-.7-8.22-5.3-5.17-4.2-8.36-5.78-11.69-4.94-3.1.78-5.94 3.92-8.56 9.95a161 161 0 0 1-6.82 13.8h-1.13zm112.89 0a30.34 30.34 0 0 0 11.27-6.27c1.55-1.36 3.32-3.46 5.34-6.29 1.05-1.46 2.15-3.1 3.41-5.04a349.73 349.73 0 0 0 2.5-3.9l.47-.75.93-1.47a89.17 89.17 0 0 1 3.25-4.86c1.05-1.43 1.82-2.23 2.44-2.46 1.02-.37 1.49.48 1.49 2.04l.01 2.11c.05 6.91-.08 11.32-.7 16.33a48.4 48.4 0 0 1-2.38 10.56h-1.07a46.47 46.47 0 0 0 2.45-10.68c.62-4.96.75-9.33.7-16.2l-.01-2.12c0-.97-.08-1.12-.15-1.1-.36.14-1.05.85-1.97 2.1a88.44 88.44 0 0 0-3.22 4.82l-.92 1.46-.48.75a1268.1 1268.1 0 0 1-2.5 3.92c-1.26 1.95-2.38 3.6-3.44 5.08-2.06 2.88-3.87 5.04-5.5 6.45a30.87 30.87 0 0 1-8.94 5.52h-2.98zm-183.72 0H69.3c3.37-3.43 5.19-8.33 5.19-15 0-18.6-.04-17.35 1.02-20.77.6-1.93 1.5-3.74 3.27-6.63.42-.7 4.92-7.8 6.78-10.86 3.04-4.97 11.04-16.5 12.21-18.56 3.48-6.08 4.72-12.06 4.72-24.18 0-7.85 2.5-14.2 8.1-23.44l2.84-4.63a72.67 72.67 0 0 0 2.49-4.4c1.62-3.15 2.48-5.78 2.62-8.28.2-3.78-1.3-7.29-4.9-10.9-5.13-5.12-8.6-5.43-11.2-1.85-2.12 2.92-3.48 7.74-5.06 16.47-.2 1.03-.82 4.6-.82 4.57-.83 4.67-1.4 7.33-2.1 9.6-1.35 4.42-3.7 7.61-8.36 12.26l-3.26 3.2c-6.38 6.39-9.68 11.51-11.36 19.5l-1.16 5.52c-.87 4.1-1.56 7.04-2.33 9.94-3.67 13.74-9.65 25.97-22.59 44.72-7.68 11.14-11.05 18.87-10.92 23.72h-1c-.12-5.16 3.35-13.05 11.1-24.28 12.87-18.67 18.8-30.8 22.44-44.42.77-2.88 1.45-5.8 2.32-9.89l1.16-5.51c1.73-8.22 5.13-13.5 11.64-20 .63-.64 2.84-2.8 3.25-3.21 4.57-4.54 6.82-7.62 8.12-11.84a81.58 81.58 0 0 0 2.07-9.48l.81-4.57c1.62-8.9 3-13.8 5.24-16.89 3-4.15 7.2-3.78 12.71 1.74 3.8 3.8 5.42 7.58 5.2 11.66-.15 2.66-1.05 5.41-2.73 8.68a73.6 73.6 0 0 1-2.52 4.46l-2.84 4.63c-5.52 9.1-7.96 15.3-7.96 22.92 0 12.28-1.28 18.43-4.85 24.68-1.2 2.1-9.21 13.65-12.22 18.58-1.87 3.06-6.37 10.18-6.78 10.86-1.73 2.82-2.6 4.57-3.17 6.4-1.02 3.28-.98 2.1-.98 20.48 0 6.52-1.7 11.44-4.82 15zM310.09 0h1.06c-.37.9-.77 1.83-1.2 2.82-3.9 9.06-5.45 15.15-5.45 25.18 0 7.64-2.1 11.6-6.64 13.05-3.46 1.1-5.72.98-17.57-.43-11.55-1.36-19.17-1.58-28.16-.14-6.24 2.49-25.91 7.02-32.13 7.02-11.15 0-36.76-2.88-54.12-7.01a22.08 22.08 0 0 0-16.95 2.48c-4.05 2.33-7.09 5.03-13.9 11.97-6.28 6.39-9.53 9.23-13.8 11.5-7.09 3.79-11.22 7.65-13.4 12.27-1.82 3.85-2.33 7.84-2.33 15.29 0 4.4-2.65 6.69-9.45 9.74.1-.05-2.97 1.31-3.84 1.71-8.78 4.06-12.71 8.29-12.71 16.55 0 12.52-4.86 19.22-17.34 27.96l-4.56 3.14c-1.9 1.3-3.3 2.3-4.67 3.3-.92.68-1.79 1.34-2.62 2-7.16 5.62-11 14.54-15.56 33.28-.63 2.57-3.3 14-4.07 17.14a350.44 350.44 0 0 1-5.2 19.33c-1.37 4.5-4.5 15.07-4.96 16.53-1.05 3.4-1.64 4.94-2.46 6.32-.82 1.4-6.85 9.08-12.64 18.27L0 277.98v-1.9l4.58-7.35a270.8 270.8 0 0 1 12.61-18.23c-.3.5 1.35-2.8 2.38-6.12.45-1.44 3.58-12.01 4.95-16.53 1.83-6.03 3.44-12.09 5.19-19.27.76-3.13 3.44-14.56 4.06-17.14 4.62-18.95 8.52-28.02 15.92-33.83.84-.67 1.72-1.33 2.65-2.01 1.38-1.02 2.8-2.01 4.7-3.32l4.54-3.14C73.83 140.57 78.5 134.13 78.5 122c0-8.74 4.2-13.26 13.29-17.45.88-.41 3.96-1.77 3.85-1.73 6.46-2.9 8.86-4.97 8.86-8.82 0-7.6.53-11.7 2.42-15.71 2.29-4.84 6.57-8.85 13.84-12.73 4.15-2.21 7.35-5 14.15-11.93 6.28-6.4 9.36-9.13 13.52-11.53a23.07 23.07 0 0 1 17.69-2.59c17.27 4.12 42.8 6.99 53.88 6.99 6.1 0 25.73-4.53 31.92-7 9.12-1.46 16.83-1.25 28.49.13 11.63 1.38 13.9 1.5 17.15.47 4.06-1.3 5.94-4.85 5.94-12.1 0-10.1 1.56-16.3 6.6-28zm25.12 0h1c.05 5.62.26 11.48.65 19.4.47 9.7.64 14.57.64 21.6 0 9.81-4.68 17.46-13.1 23.16-6.53 4.43-14.94 7.46-24.33 9.33-3.74.54-9.42.56-22.68.23-6.74-.17-9.35-.22-12.39-.22-2.77 0-4.97.43-7.63 1.36-.88.3-4.55 1.74-5.58 2.11-6.55 2.35-13.59 3.53-24.79 3.53-8.1 0-13.58-1.38-22.46-4.9l-3.18-1.25c-12.55-4.87-21.27-5.15-37.18 1.12-11.15 4.39-18.13 9.2-22.28 14.81-3.15 4.26-4.33 7.8-5.94 15.8-1.22 6.09-1.93 8.74-3.5 12.13-1.65 3.53-3.97 5.81-7.07 7.22-2.33 1.07-4.35 1.5-9.32 2.19-9.04 1.27-12.77 3.09-15.61 9.58-3.71 8.48-7.72 13.87-14.22 19.76-2.4 2.18-13.14 11.02-15.91 13.42-8.2 7.1-13.85 17.37-18.7 31.97a258.81 258.81 0 0 0-3.27 10.7c-.01.05-2.26 7.97-2.88 10.1-8.49 28.85-17.88 52.95-26.13 61.2-2.8 2.8-5.06 5.64-10.4 12.96-3.4 4.68-6.23 8.25-8.95 11.1v-1.55c2.74-2.98 5.73-6.82 9.48-11.97 4.03-5.52 6.32-8.4 9.17-11.24 8.07-8.08 17.44-32.14 25.87-60.8.62-2.1 2.86-10.03 2.88-10.08 1.21-4.24 2.21-7.53 3.28-10.74 4.9-14.75 10.63-25.16 19-32.4 2.78-2.42 13.5-11.25 15.89-13.4 6.4-5.8 10.32-11.09 13.97-19.43 1.68-3.83 4.05-6.31 7.2-7.86 2.4-1.17 4.64-1.67 9.53-2.36 4.54-.63 6.5-1.05 8.7-2.06 2.89-1.31 5.03-3.42 6.58-6.73 1.53-3.3 2.23-5.9 3.43-11.9 1.64-8.14 2.85-11.79 6.11-16.2 4.28-5.79 11.41-10.7 22.73-15.16 16.15-6.36 25.13-6.07 37.9-1.11l3.19 1.26c8.77 3.47 14.13 4.82 22.09 4.82 11.09 0 18.02-1.16 24.46-3.47 1-.36 4.68-1.8 5.58-2.11A22.5 22.5 0 0 1 265 72.5c3.05 0 5.67.05 14.07.26 11.53.29 17.2.27 20.83-.25 9.25-1.85 17.54-4.83 23.94-9.17C332 57.8 336.5 50.46 336.5 41c0-7-.17-11.86-.7-22.7-.35-7.26-.55-12.83-.59-18.3zM93.87 0h2.04c-.7 4-1.61 6.82-3.03 9.47-2.33 4.38-2.85 5.75-5.26 13.03a40.46 40.46 0 0 1-1.94 5.03c-2.24 4.66-5.92 8.8-13.07 14.26-8.01 6.13-14.27 16.55-20.03 31.55-2.4 6.23-8.75 25.63-9.64 28.01-2.69 7.16-6.56 12.7-15.63 23.68l-2.68 3.24c-6.02 7.34-9.35 12.07-11.72 17.15-2.3 4.94-7.12 9.9-12.91 14.15v-2.4c5.14-3.94 9.1-8.3 11.1-12.6 2.46-5.27 5.87-10.1 11.98-17.56l2.68-3.26c8.94-10.8 12.72-16.22 15.3-23.1.88-2.33 7.24-21.74 9.65-28.03 5.89-15.31 12.3-26 20.68-32.41 6.92-5.3 10.4-9.2 12.48-13.55.65-1.35 1.16-2.7 1.85-4.79 2.45-7.4 3-8.83 5.4-13.34A27.68 27.68 0 0 0 93.87 0zm9.07 0h1.02c-1.66 8.3-2.91 12.67-4.54 15.26a59.14 59.14 0 0 0-4.1 8.21c-1.27 3-2.44 6.2-3.5 9.4-.38 1.12-.7 2.16-2.41 5.39a251.48 251.48 0 0 0-12.81 13.3c-3.48 3.96-5.95 7.27-7.15 9.66-.95 1.9-2.06 5.99-3.61 12.97-.64 2.9-3.65 17.15-4.51 21.07-3.63 16.45-6.63 26.69-9.9 32-7.66 12.45-10.64 15.71-37.08 41.1A69.78 69.78 0 0 1 0 179.21v-1.15a69.39 69.39 0 0 0 13.65-10.42c26.4-25.33 29.32-28.55 36.92-40.9 3.2-5.18 6.18-15.37 9.78-31.7.86-3.91 3.87-18.16 4.51-21.06 1.57-7.09 2.7-11.2 3.7-13.2 1.24-2.5 3.76-5.86 7.29-9.89.9-1.03 1.86-2.1 2.86-3.18 2.4-2.6 4.96-5.22 7.53-7.76.9-.88 1.73-1.7 3.37-3.4a129.02 129.02 0 0 1 4.78-13.46 60.07 60.07 0 0 1 4.19-8.35c1.52-2.44 2.74-6.71 4.36-14.74zM83.71 0h1.1c-2.09 4.74-6.03 8.92-11.42 12.3-7.2 4.52-16.5 7.2-24.39 7.2-8.9 0-11.8 7-11.74 21.52 0 1.7.04 3.17.12 5.99.1 3.3.12 4.45.12 5.99 0 5.73-.76 11.3-2.01 16.5a66.67 66.67 0 0 1-2.15 6.97 2597.76 2597.76 0 0 1-7 15.86A4270.8 4270.8 0 0 1 6.44 136.2 54.64 54.64 0 0 1 0 147v-1.65a54.87 54.87 0 0 0 5.55-9.57A4269.82 4269.82 0 0 0 30.7 79.97c.53-1.2.99-2.23 2.44-5.9A69.23 69.23 0 0 0 36.5 53c0-1.52-.03-2.66-.12-5.95-.08-2.83-.12-4.31-.12-6.01-.03-6.79.53-11.62 2.07-15.34 1.94-4.68 5.39-7.19 10.67-7.19 7.7 0 16.81-2.63 23.86-7.05C77.93 8.27 81.66 4.38 83.7 0zm282.63 0h1.01c1.86 10.02 2.18 12.67 2.32 18.3a123.43 123.43 0 0 1 .37 27.83c-.96 8.78-3.1 16.01-6.63 21.15-11.34 16.5-39.8 29.22-66.41 29.22-5.09 0-10.47.28-16.31.83a413.8 413.8 0 0 0-24.37 3.16c-21.56 3.26-27.66 4.01-36.32 4.01-6.92 0-12.2-1.05-21.69-3.9l-2.78-.83c-1.39-.41-2.54-.74-3.65-1.02-8-2.05-14.22-2.04-21.7.72a16.32 16.32 0 0 0-9.17 8.18c-1.6 3.05-2.5 6.06-4.02 12.83-1.5 6.64-2.34 9.52-3.99 12.64a16.16 16.16 0 0 1-9.85 8.36 104.8 104.8 0 0 0-9.5 3.42c-6.55 2.8-10.1 5.57-13.8 10.47-1.33 1.75-1.03 1.3-5.43 7.9-1.98 2.97-4.66 5.8-8.48 9.14-2.01 1.76-10.71 8.83-12.88 10.7-7.37 6.35-12.58 12.14-16.63 19.14-4.22 7.3-7.8 18.3-11.28 33.26-.87 3.73-1.72 7.64-2.64 12.14l-1.18 5.8-1.09 5.45c-1.8 8.96-2.77 13.28-3.77 16.26-6.8 20.44-17.26 42.16-27.13 51.2-5.11 4.7-8.1 7.07-11.1 8.86-.9.54-1.84 1.04-2.92 1.57-.44.22-9.6 4.4-14.1 6.66l-1.22.62v-1.13l.78-.39c4.52-2.26 13.67-6.44 14.1-6.65a41.19 41.19 0 0 0 2.84-1.54c2.94-1.75 5.88-4.09 10.94-8.73 9.71-8.9 20.1-30.51 26.87-50.79.97-2.92 1.94-7.22 3.73-16.13l1.1-5.46a490.5 490.5 0 0 1 3.82-17.96c3.5-15.06 7.1-26.14 11.39-33.54 4.11-7.11 9.4-12.98 16.83-19.4 2.19-1.88 10.88-8.95 12.88-10.7 3.77-3.28 6.39-6.05 8.3-8.93 4.43-6.64 4.12-6.18 5.47-7.96 3.8-5.03 7.5-7.91 14.21-10.78 2.61-1.12 5.74-2.24 9.59-3.46a15.17 15.17 0 0 0 9.27-7.86c1.59-3.02 2.42-5.85 4.03-12.99 1.41-6.27 2.32-9.33 3.98-12.48a17.31 17.31 0 0 1 9.7-8.66c7.7-2.83 14.1-2.84 22.3-.75 1.12.29 2.28.61 3.68 1.03l3.73 1.11c8.47 2.54 13.66 3.58 20.46 3.58 8.59 0 14.67-.75 36.18-4a414.64 414.64 0 0 1 24.41-3.17c5.88-.54 11.29-.83 16.41-.83 26.3 0 54.45-12.58 65.59-28.78 3.42-4.98 5.5-12.06 6.46-20.7.84-7.74.73-16.02.02-23.9a136.2 136.2 0 0 0-.57-5.12c0-4.47-.3-6.94-2.16-17zM18.88 0h1.03C18 7.57 17.15 10.18 14.46 16.2c-1.95 4.37-2.67 9.19-2.42 14.89.2 4.33.71 7.7 2.28 16.13 1.09 5.88 1.57 8.77 1.94 12.2.96 8.9.24 16.08-2.8 22.79A463.4 463.4 0 0 1 0 109.43v-2.12a465 465 0 0 0 12.54-25.52c2.97-6.52 3.67-13.53 2.72-22.27-.36-3.4-.84-6.26-1.93-12.12-1.57-8.47-2.1-11.88-2.29-16.27-.26-5.84.48-10.81 2.5-15.33 2.64-5.9 3.48-8.47 5.34-15.8zm280.47 0a70.78 70.78 0 0 1-4.91 11.24c-2.56 4.7-4.01 8.45-4.86 11.98l-.4 1.8-.28 1.45a5.28 5.28 0 0 1-.74 2.07c-.74 1.03-1.93 1.28-5.13 1.25.92 0-9.85-.29-15.03-.29-10.2 0-18.45.82-29.46 2.56-16.87 2.66-17.73 2.77-23.66 2.52a42.57 42.57 0 0 1-8-1.09c-17.7-4.16-46.18-5.86-54.72-3.01-2.72.9-5.88 2.8-9.52 5.59a112.37 112.37 0 0 0-6.54 5.48c-1.4 1.25-9.17 8.5-10.78 9.84-1.45 1.2-8.18 7.42-8.85 8.02a114.65 114.65 0 0 1-4.55 3.9c-4.99 4.03-8.9 6.2-11.92 6.2-3.52.05-4.32 0-5.14-.4-1.13-.56-1.5-1.72-1.13-3.57.74-3.63 4.47-10.84 12.84-24.8 5.69-9.48 9.42-18 11.78-26.2 1.45-5.04 1.94-7.4 2.97-14.54h1.01c-1.05 7.3-1.54 9.7-3.01 14.82-2.39 8.28-6.16 16.89-11.9 26.44-8.3 13.84-12 21.01-12.7 24.48-.3 1.45-.08 2.14.59 2.47.6.3 1.35.35 3.48.3 3.92 0 7.69-2.1 12.5-5.98 1.4-1.13 2.87-2.39 4.51-3.86.66-.59 7.41-6.83 8.88-8.05 1.59-1.33 9.34-8.55 10.75-9.82 2.4-2.15 4.55-3.96 6.6-5.53 3.72-2.85 6.97-4.8 9.81-5.74 8.76-2.92 37.41-1.22 55.27 2.99 2.57.6 5.14.95 7.81 1.06 5.84.25 6.7.14 23.47-2.51 11.05-1.75 19.36-2.57 29.6-2.57 5.2 0 15.99.3 15.05.29 2.87.03 3.84-.17 4.3-.83.23-.32.4-.8.58-1.7l.28-1.43.4-1.85c.88-3.6 2.36-7.44 4.96-12.22 1.87-3.43 3.44-7 4.73-10.76h1.06zm-8.59 0c-5.91 17.94-9.55 22-19.76 22-4.5 0-10.22.32-28.69 1.5l-1.53.1c-15.6.99-23.47 1.4-28.78 1.4-5.35 0-13.24-.96-28.86-3.28l-1.54-.23C163.18 18.75 157.47 18 153 18c-4.45 0-7.3 1.01-10.96 3.34-.1.06-1.8 1.17-2.3 1.47-2.43 1.5-4.32 2.19-6.74 2.19-2.8 0-4.11-1.46-4.11-4.22 0-1.04.16-2.29.5-4.1.16-.82.9-4.4 1.07-5.32.8-4.11 1.3-7.68 1.47-11.36h2c-.17 3.82-.68 7.5-1.5 11.75-.19.94-.92 4.5-1.07 5.31a21.04 21.04 0 0 0-.47 3.72c0 1.7.46 2.22 2.11 2.22 1.99 0 3.55-.57 5.7-1.9.47-.28 2.15-1.37 2.26-1.44C144.92 17.14 148.12 16 153 16c4.62 0 10.3.74 28.9 3.51l1.53.23C198.93 22.04 206.8 23 212 23c5.25 0 13.11-.41 28.65-1.4l1.54-.1C260.73 20.32 266.43 20 271 20c8.95 0 12.15-3.4 17.66-20h2.1zM141.51 0h1.13c-2.06 3.86-2.63 5.1-2.77 6.19-.15 1.12.42 1.64 2.32 1.96 1.8.3 3.85.35 10.81.35 6.02 0 13 .56 21.35 1.62 3.95.5 8.03 1.1 13.13 1.89 24 3.7 22.5 3.49 26.83 3.49 24.02 0 51.83-2.24 60.45-6.94 2.88-1.57 5.05-4.49 6.6-8.56h1.07c-1.64 4.47-3.98 7.69-7.2 9.44-8.83 4.82-36.67 7.06-60.92 7.06-4.41 0-2.84.22-26.98-3.5-5.1-.8-9.17-1.38-13.1-1.88-8.31-1.06-15.26-1.62-21.23-1.62-7.04 0-9.1-.05-10.97-.37-2.38-.4-3.38-1.32-3.15-3.07.16-1.22.69-2.41 2.63-6.06zm76.4 0c5.69 1.64 10.37 2.5 14.09 2.5 9.59 0 16.7-.71 22.4-2.5h2.98C251.12 2.53 243.2 3.5 232 3.5c-4.5 0-10.32-1.21-17.53-3.5h3.45zM70.69 0c-2.87 3.27-6.95 5.39-12.02 6.53-3.98.89-7.5 1.08-12.92 1A97.24 97.24 0 0 0 44 7.5c-5.37 0-8.86-1.24-10.1-4.97A8.6 8.6 0 0 1 33.5 0h.99c.02.82.14 1.56.36 2.22C35.91 5.39 39.02 6.5 44 6.5l1.76.02c5.35.09 8.8-.1 12.69-.97C62.95 4.54 66.63 2.74 69.3 0h1.37zM0 207.87c7.31-.16 11.5 3.33 11.5 11.13 0 11.41-5.05 28.35-11.5 41.5v-2.3c5.93-12.72 10.5-28.47 10.5-39.2 0-7.18-3.7-10.3-10.5-10.13v-1zm0 7.05c1.23.14 2.18.58 2.87 1.31 1.4 1.48 1.6 3.72 1.16 7.58l-.16 1.3A28.93 28.93 0 0 0 3.5 229c0 3.2-1.48 9.52-3.5 15.9v-3.45c1.49-5.13 2.5-9.87 2.5-12.45 0-.98.08-1.75.37-4.02l.16-1.29c.42-3.56.24-5.59-.88-6.77-.5-.53-1.21-.87-2.15-1v-1zM0 410.9v-1.47a21.67 21.67 0 0 0 2.97-4.7c1.32-2.7 2.68-6.28 4.56-11.89 7.85-23.55 7.83-26.6.25-30.4-2.25-1.12-4.8-1.43-7.78-.91v-1.02a13.1 13.1 0 0 1 8.22 1.04c8.24 4.12 8.26 7.6.25 31.6-1.88 5.66-3.25 9.27-4.6 12.02A20.82 20.82 0 0 1 0 410.9zM33.64 452c1.68 0 3.04-.23 8.34-1.31l2.38-.47c8.26-1.57 12.72-1.3 14.53 2.33 1.38 2.75-.47 5.86-4.75 9.68a75.6 75.6 0 0 1-5.08 4.07c-.94.7-4.89 3.59-5.79 4.27-1.86 1.4-2.97 2.37-3.47 3.03a19.08 19.08 0 0 0-2.89 5.5c.07-.2-4.02 13.65-6.96 22.22-2.7 7.85-5.56 10.72-8.82 8.59-2.11-1.4-3.66-4.24-6.6-11.03-1.98-4.62-2.5-5.76-3.4-7.4-4.55-8.18-3.9-23.9-.05-32.87a9.6 9.6 0 0 1 6.98-5.96c2.59-.66 4.86-.75 11.78-.67l3.8.02zm0 2c-1.13 0-2.09 0-3.82-.02-12.07-.13-14.83.57-16.9 5.41-3.63 8.47-4.26 23.55-.05 31.12.96 1.73 1.48 2.88 3.5 7.58 2.72 6.3 4.24 9.08 5.86 10.14 1.64 1.08 3.5-.8 5.82-7.55a682.9 682.9 0 0 0 6.97-22.24 21.03 21.03 0 0 1 3.18-6.04c.65-.87 1.85-1.9 3.86-3.43.92-.7 4.87-3.57 5.8-4.27 2.02-1.5 3.6-2.77 4.95-3.97 3.63-3.23 5.09-5.7 4.3-7.28-1.21-2.42-5.07-2.65-12.38-1.27l-2.35.47c-5.49 1.11-6.86 1.35-8.74 1.35zm345.63 146c-3.45-12.26-3.77-14.13-3.77-19 0-3.33-.13-6.27-.43-11.34-.63-10.33-.65-13.5.26-17.07 1.21-4.74 4.21-7.1 9.67-7.1h26c4.08 0 5.19 1.85 5.93 7.11.1.79.13.97.19 1.32.84 5.35 2.8 7.58 8.88 7.58 3.64 0 5.54.4 6.43 1.37.76.83.76 1.44.36 3.93-.85 5.26.5 8.85 7.5 13.8 6.32 4.45 11.63 5.36 16.55 3.37 3.8-1.54 6.73-4.16 11.92-10l1.1-1.23 1.09-1.23a75.6 75.6 0 0 1 2.7-2.86 35.81 35.81 0 0 1 9.57-6.73c1.52-.76 1.72-.86 5.66-2.63 6.1-2.73 9.01-4.5 11.74-7.62 2.63-3 4.67-4.85 6.7-6.04 3.18-1.85 5.46-2.13 13.68-2.13 5.98 0 10.56-4.32 18-14.99l2.82-4.03c1.06-1.5 1.94-2.7 2.79-3.79 7.87-10.12 19.38-10.4 30.74.96 5.54 5.53 10.17 19.43 13.64 38.51 2.5 13.75 4.18 29.46 4.47 39.84h-1c-.3-10.32-1.96-25.97-4.45-39.66-3.43-18.87-8.02-32.65-13.36-37.99-10.95-10.95-21.76-10.68-29.26-1.04-.83 1.07-1.7 2.26-2.75 3.75l-2.81 4.02c-7.65 10.95-12.38 15.42-18.83 15.42-8.04 0-10.21.26-13.17 2-1.92 1.12-3.9 2.9-6.45 5.83-2.86 3.26-5.87 5.09-12.09 7.88a103.35 103.35 0 0 0-5.62 2.6 34.84 34.84 0 0 0-9.32 6.54 74.67 74.67 0 0 0-3.75 4.05l-1.1 1.24c-5.28 5.95-8.29 8.64-12.28 10.25-5.26 2.13-10.92 1.17-17.5-3.48-7.33-5.17-8.82-9.15-7.92-14.77.34-2.12.34-2.6-.1-3.1-.64-.69-2.34-1.04-5.7-1.04-6.63 0-8.96-2.63-9.87-8.42l-.2-1.34c-.67-4.82-1.53-6.24-4.93-6.24h-26c-5 0-7.6 2.04-8.7 6.34-.88 3.43-.85 6.57-.23 16.76a177 177 0 0 1 .43 11.4c0 4.78.32 6.63 3.81 19h-1.04zm13.68 0c-1.31-6.58-1.61-10.71-1.36-14.84.04-.7.1-1.44.18-2.38l.23-2.56c.34-3.81.5-6.97.5-11.22 0-4.94 1.46-7.76 4.21-8.42 2.38-.58 5.56.54 9.2 3 6.64 4.52 13.99 13.07 16.55 19.23 4.77 11.44 14.12 15.69 33.54 15.69 8.6 0 14.32-2.35 20.67-7.88 1.45-1.26 15.06-15 21-20 7.21-6.07 11.77-7.59 20.62-8.32 5.52-.45 7.98-.9 11.44-2.36 4.58-1.95 9.36-5.48 14.9-11.29 7.43-7.76 13.25-8.92 17.47-4.3 3.32 3.63 5.46 10.58 6.82 20.24.73 5.17.94 7.74 1.58 17.38.25 3.75.17 5.32-.92 18.03h-1c1.09-12.7 1.17-14.28.92-17.97-.64-9.6-.85-12.16-1.57-17.3-1.33-9.47-3.43-16.27-6.56-19.7-3.76-4.11-8.93-3.08-16 4.32-5.65 5.9-10.54 9.5-15.25 11.5-3.58 1.53-6.13 1.99-11.6 2.44-8.8.72-13.17 2.18-20.2 8.1-5.9 4.96-19.5 18.7-21 19.99-6.52 5.68-12.47 8.12-21.32 8.12-19.78 0-29.5-4.42-34.46-16.3-2.49-5.97-9.71-14.38-16.2-18.79-3.42-2.32-6.36-3.35-8.4-2.86-2.2.53-3.44 2.92-3.44 7.45 0 4.28-.16 7.47-.5 11.31l-.23 2.56c-.09.93-.14 1.65-.19 2.35-.24 4.08.06 8.18 1.39 14.78h-1.02zm113.75 0c2.52-3.26 8.93-11.79 10.9-14.3 5.48-6.98 13.05-12.38 19.4-13.94 7.01-1.71 11.5 1.45 11.5 9.24 0 4.02-.04 5.16-.74 19h-1c.7-13.85.74-15 .74-19 0-7.12-3.86-9.83-10.26-8.26-6.11 1.5-13.5 6.77-18.85 13.57-1.86 2.36-7.65 10.07-10.43 13.69h-1.26zm-9.86-338.96c3.44 2.71 7 5.1 11.44 7.75 1.06.64 8.42 4.9 10.35 6.1 11.27 7 15 13.35 12.35 25.33-1.45 6.52-4.53 11.1-9.39 14.44-3.83 2.63-8.07 4.26-16.08 6.56-11.97 3.45-13.68 3.99-18.82 6.28a60.18 60.18 0 0 0-7.81 4.18c-11.11 7.07-19.1 7.7-27.96 3.28-3.56-1.77-17.2-11-17.2-11.01a101.77 101.77 0 0 0-5.2-3.07c-16.04-8.83-34.27-24.16-34.52-31.85-.11-3.46 1.99-6.57 6.28-10.26 1.03-.9 2.18-1.81 3.68-2.95.72-.55 3.38-2.56 3.94-3 4.47-3.4 7.18-5.79 9.32-8.45 11.12-13.82 26.55-28.68 34.36-32.28 12.06-5.54 19.84-5.77 27.37.12 3.25 2.54 5.65 6.54 8.58 13.35.29.65 2.3 5.45 2.88 6.74 1.62 3.65 2.9 5.8 4.24 6.94.72.6 1.45 1.2 2.2 1.8zm-3.49-.28c-1.63-1.39-3.03-3.74-4.77-7.65-.58-1.3-2.6-6.12-2.88-6.76-2.81-6.5-5.08-10.3-7.98-12.56-6.83-5.35-13.85-5.15-25.3.12-7.45 3.42-22.7 18.12-33.64 31.72-2.27 2.82-5.08 5.3-9.67 8.79l-3.94 2.98a79.98 79.98 0 0 0-3.59 2.88c-3.87 3.33-5.67 6-5.58 8.69.21 6.64 18.14 21.72 33.48 30.15 1.76.97 3.5 2 5.3 3.13.12.08 13.61 9.22 17.03 10.92 8.22 4.1 15.46 3.52 26-3.18a62.17 62.17 0 0 1 8.07-4.31c5.25-2.35 7-2.9 19.08-6.38 7.8-2.24 11.9-3.82 15.5-6.3 4.44-3.04 7.23-7.18 8.56-13.22 2.44-11.02-.83-16.6-11.45-23.2-1.9-1.18-9.23-5.42-10.32-6.08-4.5-2.69-8.13-5.12-11.64-7.9-.77-.6-1.52-1.21-2.26-1.84zM87.72 241.6c4.3-2.98 7.88-5 12.14-6.95.84-.4 1.73-.78 2.78-1.24l4.37-1.88a164.3 164.3 0 0 0 17.74-8.96 320.67 320.67 0 0 1 27.87-14.5c4.22-1.95 21.89-9.84 21.17-9.52 19.17-8.62 28.1-6.93 49.5 8.05 7.91 5.54 13.24 13.25 16.45 22.66 3.02 8.83 3.76 16.51 3.76 27.75 0 8.32-.66 12.95-3.68 18.97-4.18 8.36-12.3 16.14-25.58 23.47-24.45 13.49-38.83 27.55-52.83 47.84-8.83 12.8-47.76 44.21-65.16 54.15C75.04 413.55 48.89 423.5 31 423.5c-10.05 0-14.67-4.78-14.76-13.37-.07-6.32 2.06-13.73 6.3-24.32 2.95-7.37 2.02-12.9-2.16-22.29-3.19-7.17-3.88-9.14-3.88-12.52 0-3.35 1.87-6.9 5.52-11.07 2.61-3 3.5-3.83 11.9-11.5 5.09-4.66 8.08-7.6 10.7-10.75 9.46-11.36 12.62-19.47 17.9-44.78 3.12-15.05 6.63-20.28 15.12-25.25.8-.47 3.95-2.25 4.7-2.68a76.66 76.66 0 0 0 5.38-3.38zm.56.82a77.63 77.63 0 0 1-5.44 3.43l-4.7 2.67c-8.23 4.82-11.57 9.81-14.65 24.6-5.3 25.45-8.51 33.7-18.1 45.21-2.66 3.19-5.68 6.16-10.8 10.84-8.36 7.64-9.24 8.48-11.82 11.42-3.5 4.01-5.27 7.36-5.27 10.42 0 3.18.68 5.1 3.8 12.12 4.27 9.6 5.24 15.37 2.16 23.07-4.18 10.47-6.29 17.78-6.22 23.93.08 8.06 4.26 12.38 13.76 12.38 17.67 0 43.68-9.9 64.75-21.93 17.28-9.88 56.1-41.2 64.84-53.85 14.08-20.42 28.57-34.59 53.17-48.16 13.12-7.23 21.09-14.87 25.17-23.03 2.92-5.86 3.57-10.35 3.57-18.53 0-11.13-.74-18.73-3.7-27.43-3.15-9.22-8.36-16.75-16.09-22.16-21.13-14.8-29.7-16.42-48.5-7.95.7-.32-16.96 7.56-21.17 9.5-1.7.8-3.3 1.55-4.86 2.3a319.68 319.68 0 0 0-22.93 12.17 165.3 165.3 0 0 1-17.85 9.01l-4.37 1.88c-1.04.45-1.92.84-2.76 1.23a74.56 74.56 0 0 0-11.99 6.86zm-7.6 12.2c7.7-6.25 12.3-8.17 23.68-11.27 6.12-1.67 9.12-2.95 12.31-5.72 3.8-3.3 7.47-4.52 15.86-6.1 2.75-.52 3.67-.7 5.06-1.02 5.48-1.24 9.48-2.93 13.1-5.89 10.42-8.53 25.4-14.11 36.31-14.11 5.33 0 16.77 7.58 25.74 17.16 10.73 11.46 15.96 23.27 12.73 32.5-3.18 9.1-11.39 18.57-23.03 27.86-8.44 6.73-18.36 13-25.22 16.43-3.72 1.86-6.59 4.88-9.77 9.99-.69 1.1-11.1 20.25-16.03 27.83-5.62 8.65-15.4 17.36-30.23 27.96a552.58 552.58 0 0 1-9.2 6.42c-.13.09-6.81 4.65-8.6 5.89-6.47 4.46-10.35 7.35-13.05 9.83-11.64 10.67-37.14 15.54-43.7 8.98-1.96-1.96-2.2-4.06-1.95-10.52.37-9.42-.5-14.5-4.95-20.51a34.09 34.09 0 0 0-7.04-6.92c-3.93-2.95-6.07-6.11-6.56-9.49-.97-6.61 3.87-13.06 14.17-21.69 1.58-1.32 6.67-5.44 7.09-5.78a48.03 48.03 0 0 0 5.23-4.77c4.1-4.63 5.85-9.55 7.8-20.07a501.52 501.52 0 0 0 .8-4.37c.33-1.87.6-3.3.88-4.73.74-3.78 1.5-7.18 2.4-10.63 1-3.78 1.38-5.5 2.36-10.37.6-3.02.93-4.21 1.56-5.47 1.22-2.45 1.27-2.5 12.25-11.42zm.64.78c-10.77 8.74-10.88 8.84-12 11.08-.58 1.16-.88 2.3-1.47 5.22-.98 4.89-1.36 6.63-2.37 10.44-.9 3.43-1.65 6.8-2.39 10.56a339.79 339.79 0 0 0-1.29 6.95l-.39 2.15c-1.98 10.68-3.77 15.74-8.04 20.54a48.77 48.77 0 0 1-5.34 4.88c-.42.34-5.5 4.47-7.07 5.78-10.04 8.4-14.72 14.65-13.83 20.78.45 3.1 2.44 6.03 6.17 8.83 3 2.25 5.39 4.62 7.24 7.12 4.63 6.24 5.52 11.52 5.15 21.15-.25 6.14-.01 8.1 1.66 9.78 6.1 6.1 31.02 1.33 42.31-9.02 2.75-2.52 6.66-5.43 13.16-9.92l8.6-5.89c3.63-2.48 6.45-4.44 9.19-6.4 14.73-10.54 24.44-19.18 29.97-27.7 4.9-7.54 15.31-26.68 16.02-27.8 3.27-5.26 6.26-8.41 10.18-10.37 6.79-3.4 16.65-9.63 25.03-16.32 11.52-9.18 19.61-18.53 22.72-27.4 3.07-8.78-2.02-20.27-12.52-31.49-8.8-9.4-20.04-16.84-25.01-16.84-10.67 0-25.43 5.5-35.68 13.89-3.76 3.07-7.9 4.81-13.5 6.09-1.41.32-2.35.5-5.11 1.02-8.21 1.55-11.76 2.73-15.38 5.88-3.34 2.9-6.45 4.22-12.7 5.92-11.26 3.07-15.75 4.94-23.31 11.09zM212 251.85c0 7.56-.6 10.92-2.6 14.3-1.1 1.84-7.66 10.05-8.6 11.3-5.96 7.94-9.33 10.28-17.26 13.76-1.34.58-2.2 1-3.03 1.5-.55.33-1.2.66-2 1.02-.71.33-4.46 1.9-5.52 2.39-6.05 2.78-8.99 5.8-8.99 10.73 0 10.97-18.95 36.12-34.51 44.87-8.18 4.6-21.3 9.36-32.78 11.86-13.33 2.9-22.49 2.48-24.62-2.32-1.32-2.97-4.4-4.26-11.98-5.81l-.6-.12c-4.84-.99-6.94-1.55-9.03-2.64-2.92-1.5-4.48-3.7-4.48-6.84 0-2.74 1.08-5.77 3.25-9.67.85-1.53 1.82-3.13 3.23-5.35-.16.25 2.83-4.4 3.67-5.76 6.69-10.7 9.85-18.5 9.85-27.22 0-18.41 11.22-33.37 27.5-42.86 5.22-3.05 9.23-3.31 15.2-2.12 5.04 1 6.05.9 7.43-1.52 4.5-7.85 7.04-9.5 15.87-9.5 3.93 0 6.97-.98 10.47-3.16 1.56-.97 8.67-6.17 10.99-7.68 9.2-5.98 11.34-7 25.2-11.95 6.95-2.48 15.18 1.28 22.33 9.12 6.55 7.19 11.01 16.61 11.01 23.67zm-2 0c0-6.5-4.25-15.48-10.49-22.32-6.67-7.32-14.16-10.74-20.17-8.59-13.73 4.9-15.73 5.85-24.8 11.75-2.24 1.46-9.37 6.68-11.01 7.7-3.8 2.36-7.2 3.46-11.53 3.46-8.08 0-9.98 1.23-14.13 8.5-1.1 1.91-2.51 2.88-4.35 3.09-1.3.14-1.9.05-5.22-.61-5.53-1.1-9.07-.88-13.8 1.88-15.72 9.17-26.5 23.55-26.5 41.14 0 9.2-3.28 17.29-10.15 28.28l-3.68 5.77c-1.39 2.19-2.35 3.77-3.17 5.25-2.02 3.63-3 6.38-3 8.7 0 4.19 2.87 5.67 11.9 7.52l.61.12c8.27 1.7 11.7 3.13 13.4 6.95 3.17 7.14 36 0 54.6-10.46 14.98-8.43 33.49-32.99 33.49-43.13 0-5.9 3.47-9.48 10.16-12.55 1.1-.5 4.85-2.08 5.52-2.38.74-.34 1.32-.64 1.8-.93.92-.55 1.85-1 3.25-1.62 7.65-3.35 10.75-5.5 16.47-13.12 1.02-1.36 7.47-9.42 8.47-11.11 1.79-3.01 2.33-6.06 2.33-13.3zm-37.18-22.4c.15-.1 2.4-1.51 2.95-1.84.96-.57 1.7-.94 2.43-1.17 2.57-.83 5.06-.1 11.04 3.12 14.86 8 19.43 22.87 9.18 38.71-4.04 6.24-9.37 9-18.72 11.11-.85.2-1.2.27-3.13.68-6.04 1.29-8.78 2.08-11.6 3.65-3.63 2.02-6.09 4.98-7.5 9.44-7.87 24.93-19.72 43.34-36.28 50.31-16.45 6.93-21.13 8.53-27.98 8.89-4.94.25-9.8-.65-15.4-2.89a44.45 44.45 0 0 1-5.64-2.6c-4.02-2.33-5.14-4.74-4.5-9.31.3-2.13 3.77-15.53 4.84-20.65.63-3.05 1.19-6.14 1.75-9.69a464.04 464.04 0 0 0 1.35-8.9c1.42-9.41 2.5-14.27 4.49-18.65 2.46-5.43 6.13-9.03 11.72-11.13 6.59-2.47 10.54-3.1 18.03-3.53 4.75-.27 6.68-.64 9-2.05.61-.37 1.22-.81 1.82-1.33a30.61 30.61 0 0 0 3.37-3.4c.59-.69 2.38-2.9 2.63-3.19 3.36-4 6.3-5.53 12.33-5.53 3.94 0 5.9-.92 8.18-3.36-.17.18 2.75-3.14 3.85-4.22a30.95 30.95 0 0 1 6.79-5c1.5-.83 3.15-1.62 4.99-2.38a64.92 64.92 0 0 0 10.01-5.1zm-14.52 8.34a29.95 29.95 0 0 0-6.57 4.84 116.68 116.68 0 0 0-3.82 4.2c-2.46 2.63-4.68 3.67-8.91 3.67-5.72 0-8.39 1.39-11.57 5.17-.23.28-2.03 2.5-2.63 3.2a31.6 31.6 0 0 1-3.47 3.51c-.65.55-1.3 1.03-1.96 1.43-2.5 1.51-4.55 1.9-9.47 2.19-7.39.42-11.25 1.04-17.72 3.47-5.34 2-8.82 5.4-11.17 10.6-1.93 4.27-3 9.07-4.41 18.39l-.65 4.34-.7 4.57c-.57 3.56-1.12 6.67-1.76 9.73-1.08 5.18-4.54 18.53-4.83 20.59-.59 4.17.35 6.18 4.01 8.3 1.35.77 3.1 1.58 5.52 2.55 5.46 2.18 10.18 3.05 14.97 2.8 6.69-.34 11.32-1.93 27.65-8.8 16.21-6.83 27.92-25.01 35.71-49.7 1.49-4.7 4.12-7.86 7.97-10 2.93-1.63 5.74-2.45 11.87-3.76 1.92-.4 2.28-.49 3.12-.68 9.12-2.06 14.24-4.7 18.1-10.67 9.92-15.34 5.55-29.55-8.82-37.29-5.75-3.1-8.03-3.76-10.25-3.05-.65.2-1.33.54-2.23 1.08-.55.32-2.77 1.72-2.93 1.82a65.91 65.91 0 0 1-10.16 5.17c-1.8.75-3.42 1.52-4.89 2.33zm-42.39 32.72c16.15-2.87 26.36-.97 32.47 6.16 5.08 5.93 1.13 21.42-5.93 35.55-4.79 9.58-10.6 16.21-23.16 25.19-14.15 10.1-35.5 12.2-40.71 3.85-1.86-2.97-2.1-8.14-1.06-15.73.78-5.68 1.86-10.71 4.73-22.98l.12-.51c1.59-6.8 2.37-10.31 3.14-14.14 1.45-7.25 3.74-11.47 7.26-13.74 2.81-1.8 5.53-2.28 12.33-2.62 5.33-.27 7.56-.46 10.81-1.03zm.18.98c-3.3.59-5.56.78-10.94 1.05-6.62.33-9.23.78-11.84 2.46-3.25 2.1-5.42 6.09-6.82 13.1-.77 3.84-1.56 7.35-3.15 14.17l-.12.5c-2.86 12.24-3.93 17.26-4.7 22.9-1.03 7.36-.79 12.36.9 15.07 4.82 7.7 25.54 5.67 39.29-4.15 12.43-8.88 18.13-15.39 22.84-24.81 6.86-13.72 10.75-29 6.07-34.45-5.84-6.81-15.7-8.65-31.53-5.84zM132 276.5c7.12 0 10.66 3.08 11.25 8.7.42 4.02-.43 8.14-2.77 15.94-2.56 8.52-18.36 25.38-27.2 31.28-7.01 4.67-20.02 5.67-26.57.99-3.99-2.85-3.53-12.08.02-26.46.68-2.75 1.47-5.65 2.37-8.76a412.6 412.6 0 0 1 3.05-10.14l.37-1.2c1.48-4.8 5.1-7.75 10.73-9.27 4.4-1.2 9.54-1.5 17.48-1.33l3.89.1c3.87.11 5.42.15 7.38.15zm0 1c-1.97 0-3.53-.04-7.41-.15l-3.88-.1c-7.85-.17-12.92.13-17.2 1.3-5.32 1.43-8.67 4.16-10.03 8.6a1277.83 1277.83 0 0 1-1.6 5.21c-.68 2.2-1.27 4.17-1.82 6.1-.9 3.1-1.68 5.99-2.36 8.73-3.43 13.88-3.87 22.93-.4 25.4 6.17 4.42 18.73 3.45 25.42-1 8.66-5.78 24.33-22.49 26.8-30.73 2.3-7.67 3.14-11.71 2.73-15.56-.53-5.1-3.64-7.8-10.25-7.8zm-17.79 7a31.3 31.3 0 0 1 8.57 1.4c5.42 1.78 8.72 5.03 8.72 10.1 0 9.59-9.51 17.2-22.34 21.47-9.82 3.28-13.62-1.79-11.66-16.54.84-6.28 3.82-10.67 8.24-13.46a20.38 20.38 0 0 1 8.47-2.97zm-.6 1.08a19.39 19.39 0 0 0-7.34 2.73c-4.18 2.64-6.98 6.78-7.77 12.76-1.89 14.11 1.36 18.45 10.34 15.46C121.3 312.37 130.5 305 130.5 296c0-4.56-2.98-7.5-8.03-9.15a28.05 28.05 0 0 0-8.2-1.35c-.13 0-.35.03-.66.08zm80.87-23.45c-2.72 9.8-14.93 9.86-26.72 3.3-10.17-5.64-13.8-17.98-5-22.87a66.53 66.53 0 0 0 4.48-2.7l2.03-1.3a50.15 50.15 0 0 1 3.92-2.3c4.73-2.43 8.82-2.8 14-.72 9.16 3.66 10.98 13.33 7.3 26.6zm-20.83-24.98a49.26 49.26 0 0 0-3.84 2.25l-2.03 1.3c-.84.53-1.5.95-2.16 1.35-.82.5-1.6.96-2.38 1.39-7.94 4.4-4.59 15.8 5 21.12 11.31 6.29 22.8 6.23 25.28-2.7 3.57-12.83 1.85-21.97-6.7-25.4-4.9-1.95-8.69-1.62-13.17.7zm17.85 12.15c0 5.7-2.44 9-6.64 9.96-3.3.76-7.56-.05-11.08-1.81l-1.89-.94c-.67-.34-1.18-.62-1.63-.88-4.07-2.38-4.13-4.97.34-10.93 6.8-9.06 20.9-7.16 20.9 4.6zm-1 0c0-5.3-2.87-8.55-7.32-9.16-4.23-.57-8.99 1.44-11.78 5.16-4.15 5.54-4.1 7.44-.64 9.47.44.25.93.51 1.59.85l1.87.93c3.34 1.67 7.36 2.44 10.42 1.74 3.73-.86 5.86-3.74 5.86-9zM387 530.3c0-12.8 2.44-16.74 18.48-29.77a56.8 56.8 0 0 1 7.61-5.2c2.6-1.5 5.33-2.82 8.5-4.18 1.24-.53 2.48-1.05 4.1-1.7l3.92-1.57c9.4-3.83 13.74-6.7 16.62-12.05 1.2-2.22 2.21-4.4 3.23-6.83a148.57 148.57 0 0 0 1.54-3.84l.3-.74.56-1.44c3.2-8.02 6.05-12.08 12.7-16.5a35.26 35.26 0 0 0 4.96-4 46.36 46.36 0 0 0 3.88-4.29c.27-.34 2.55-3.2 3.2-3.98 3.48-4.15 6.51-5.9 11.51-5.9 3.08 0 5.62-.63 9.57-2.1 5.42-2.02 6.53-2.34 8.96-2.2 2.53.13 4.85 1.26 7.18 3.59 1.3 1.3 5.55 5.83 6.52 6.78 5.06 5 9.44 6.92 17.77 6.92a197.5 197.5 0 0 1 12.08.45c15.93.87 21.94.57 25.28-2.21 6.91-5.77 11.64-2.73 11.64 7.76 0 10.73-8.6 20-19 20-4.8 0-8.32 1.43-9.34 3.67-1.12 2.48.68 6.15 5.98 10.57 13.6 11.33 11.24 20.76-7.64 20.76a21.91 21.91 0 0 0-14.6 5.24c-3.28 2.71-5.8 5.86-9.85 11.82l-1.52 2.25c-3.1 4.57-5.01 7.1-7.32 9.4-6.21 6.21-9.3 7.64-13.05 6.89l-1-.23a10.82 10.82 0 0 0-2.66-.37c-1.6 0-2.41.67-8.18 6.22-4.85 4.67-8.07 6.78-11.82 6.78-1.33 0-3.46 1.15-6.45 3.45-1.27.98-2.68 2.14-4.5 3.7l-4.92 4.29a181.11 181.11 0 0 1-4.54 3.82c-9.33 7.56-15.63 10.2-20.21 6.52-2.7-2.15-4.14-4.51-4.63-7.26-.37-2.04-.26-3.63.29-7.3.87-5.85.65-8.42-1.83-11.6-2.32-2.98-2.96-3.22-3.77-2.39-.25.26-1.35 1.63-1.61 1.94-2.21 2.5-4.85 3.57-9 2.82-4.6-.84-5.57-4.11-4.72-10.09l.24-1.56c.6-3.66.68-4.93.25-5.8-.44-.86-1.9-.94-5.23.4l-.74.29c-13.78 5.54-15.26 6.09-19.43 6.67-6.03.84-9.31-1.6-9.31-7.9zm2 0c0 5 2.14 6.6 7.04 5.92 3.91-.55 5.43-1.1 18.95-6.55l.75-.3c4.17-1.66 6.7-1.54 7.76.58.71 1.43.62 2.76-.06 7l-.24 1.53c-.72 5.04-.06 7.27 3.09 7.84 3.43.62 5.38-.17 7.15-2.18.2-.23 1.34-1.66 1.68-2 1.9-1.96 3.82-1.25 6.78 2.55 2.9 3.74 3.17 6.77 2.22 13.12-1 6.75-.52 9.4 3.62 12.71 3.49 2.8 9.1.45 17.7-6.51 1.35-1.1 2.75-2.28 4.49-3.78l4.93-4.3c1.84-1.58 3.27-2.76 4.58-3.77 3.34-2.56 5.74-3.86 7.67-3.86 3.04 0 5.95-1.9 10.43-6.22l2.46-2.39c.94-.89 1.67-1.56 2.37-2.13 1.81-1.49 3.3-2.26 4.74-2.26 1.03 0 1.81.13 3.1.42.7.16.71.17.96.21 2.96.6 5.45-.55 11.23-6.33 2.2-2.2 4.06-4.65 7.09-9.11l1.52-2.25c4.15-6.11 6.76-9.37 10.22-12.24a23.9 23.9 0 0 1 15.88-5.7c16.87 0 18.62-7.01 6.36-17.23-5.9-4.92-8.12-9.41-6.52-12.93 1.42-3.12 5.67-4.84 11.16-4.84 9.25 0 17-8.34 17-18 0-8.94-2.88-10.79-8.36-6.23-3.94 3.28-9.98 3.59-26.67 2.68l-1.02-.06c-5.09-.27-7.99-.39-10.95-.39-8.88 0-13.76-2.14-19.18-7.5-1-.98-5.26-5.53-6.53-6.79-1.99-1.99-3.86-2.9-5.87-3-2.03-.12-3.06.18-8.15 2.07-4.15 1.55-6.9 2.22-10.27 2.22-4.33 0-6.84 1.46-9.98 5.2-.63.74-2.89 3.6-3.18 3.95a48.29 48.29 0 0 1-4.04 4.46 37.26 37.26 0 0 1-5.24 4.23c-6.26 4.17-8.9 7.91-11.95 15.58l-.57 1.43-.28.74a531.5 531.5 0 0 1-1.56 3.88 77.49 77.49 0 0 1-3.32 7c-3.16 5.88-7.82 8.97-17.63 12.96l-3.92 1.58c-1.6.64-2.84 1.15-4.05 1.67a79.2 79.2 0 0 0-8.3 4.08 54.8 54.8 0 0 0-7.35 5.02C391.12 514.78 389 518.21 389 530.31zm133.22-79.76c3.06 1.53 6.54 2.02 10.68 1.7 2.53-.2 4.91-.62 8.8-1.49 5.36-1.19 6.33-1.38 8.33-1.54 2.78-.23 4.82.17 6.29 1.4 1.58 1.31 1.96 2.72 1.26 4.22-.66 1.38-1.05 1.74-5.05 5.07-3.53 2.93-5.03 4.83-5.03 7.09 0 7.3 1.29 10.02 7.83 15.62 3.86 3.3 5.93 6.84 5.28 9.62-.75 3.25-4.96 5.02-12.61 5.02-7.18 0-12.7 4.61-20.03 14.68-.5.7-3.96 5.57-4.94 6.87a38.89 38.89 0 0 1-4.72 5.5c-1.06.98-2.09 1.7-3.1 2.15-2.85 1.26-5.05 1.57-9.83 1.74-7.66.27-10.87 1.45-14.98 7.1-1.58 2.17-3.11 4-4.68 5.6a42.87 42.87 0 0 1-8.65 6.69c-.15.08-10.69 6.19-14.8 8.83-3.76 2.42-6.45 2.04-8.22-.77-1.28-2.03-1.9-4.54-2.87-10.35-.84-5.08-1.27-7.08-2.06-8.93-.97-2.3-2.21-3.24-4.02-2.88-6.2 1.24-8.95 1.39-10.98.2-2.37-1.4-3.13-4.62-2.62-10.73.16-1.96-1.04-2.87-3.76-3.04-2.24-.13-4.9.2-9.94 1.12l-.69.12c-7.97 1.45-10.72 1.72-12.72.73-2.91-1.43-1.6-5.27 4.23-12.21 5.48-6.53 10.6-10.81 15.76-13.53 3.74-1.97 5.94-2.65 12.16-4.1 7.29-1.72 10.4-3.51 14.04-9.31 2.96-4.75 10.74-18.62 12.14-20.84 3.59-5.67 6.8-9.1 11.05-11.34 2.6-1.38 4.72-2.82 9.17-6.07l1.38-1.01c7.85-5.72 12.3-7.98 17.68-7.98 4.22 0 6.49 1.36 9.13 4.77.34.43 1.67 2.22 2 2.67.85 1.09 1.6 1.98 2.45 2.83a24.29 24.29 0 0 0 6.64 4.78zm-.44.9c-2.8-1.4-5-3.03-6.92-4.97-.87-.9-1.65-1.81-2.51-2.93-.35-.46-1.68-2.25-2.01-2.67-2.47-3.18-4.46-4.38-8.34-4.38-5.09 0-9.4 2.2-17.09 7.78l-1.38 1.01c-4.49 3.29-6.63 4.74-9.3 6.15-4.06 2.15-7.16 5.45-10.66 11-1.39 2.19-9.16 16.05-12.15 20.82-3.79 6.07-7.13 7.98-14.66 9.75-6.13 1.45-8.27 2.1-11.92 4.02-5.04 2.66-10.05 6.86-15.46 13.3-5.43 6.46-6.53 9.69-4.55 10.66 1.7.84 4.48.57 12.1-.81l.7-.13c5.12-.93 7.82-1.27 10.17-1.12 3.21.2 4.92 1.48 4.7 4.11-.48 5.76.2 8.64 2.13 9.78 1.73 1.02 4.34.88 10.27-.31 2.35-.47 4 .78 5.14 3.47.83 1.95 1.27 4 2.07 8.8l.06.36c.94 5.65 1.55 8.11 2.72 9.98 1.46 2.3 3.52 2.6 6.84.46 4.14-2.66 14.69-8.77 14.81-8.85a41.9 41.9 0 0 0 8.46-6.54 47.89 47.89 0 0 0 4.6-5.48c4.32-5.95 7.81-7.23 15.74-7.5 4.66-.17 6.76-.47 9.46-1.67.9-.4 1.85-1.06 2.84-1.96a38.03 38.03 0 0 0 4.6-5.36c.96-1.3 4.4-6.16 4.93-6.87 7.5-10.31 13.22-15.09 20.83-15.09 7.24 0 11.02-1.6 11.64-4.24.54-2.32-1.36-5.55-4.97-8.64-6.75-5.79-8.17-8.79-8.17-16.38 0-2.67 1.64-4.74 5.39-7.86 3.8-3.17 4.23-3.56 4.78-4.73.5-1.06.25-1.99-.99-3.03-2.23-1.85-4.72-1.65-13.76.36-3.93.87-6.35 1.3-8.94 1.5-4.3.34-7.97-.18-11.2-1.8zm-28-3.9c5.65-2.82 8.96-2.2 12.9 1.37.56.5 2.6 2.47 3.02 2.87 4.2 3.89 8.07 5.71 14.3 5.71 11.37 0 14 1.41 16.1 8.09.26.83 1.35 4.6 1.66 5.62.8 2.63 1.64 5.03 2.7 7.6 2.13 5.17 2.64 8.32 1.72 10.24-.77 1.61-2.1 2.18-5.37 2.79-2.32.43-2.8.53-3.85.85-1.85.58-3.35 1.4-4.6 2.66-1 1-2.02 2.13-3.31 3.66-.6.71-2.91 3.5-3.46 4.14-7.2 8.54-12.43 12.35-19.59 12.35-3.76 0-6.95 1.28-10.59 4-1.84 1.37-11.62 10.31-15.22 13.06a73.09 73.09 0 0 1-8.95 5.88c-4.58 2.54-7.35 3.22-8.98 2.23-1.32-.8-1.65-2.07-1.94-5.5a52.53 52.53 0 0 0-.16-1.81c-.54-4.73-2.24-6.86-7.16-6.86-7.11 0-8.85-1.23-9.73-5.41-.96-4.61-2.1-6.7-6.55-9.67-3.97-2.65-4.31-5.42-1.52-8.22 2-2 4.63-3.5 11.35-6.87 6.61-3.3 9.2-4.8 11.1-6.68a39.09 39.09 0 0 0 5.3-6.48c.98-1.5 1.83-3.04 2.88-5.13l2.12-4.3c.91-1.83 1.72-3.37 2.61-4.98 5.74-10.32 10.37-14.78 23.22-21.2zm-22.34 21.7c-.89 1.59-1.69 3.12-2.6 4.94l-2.11 4.3a52.9 52.9 0 0 1-2.94 5.23 40.08 40.08 0 0 1-5.44 6.63c-2 2-4.62 3.51-11.35 6.87-6.6 3.3-9.2 4.8-11.1 6.69-2.33 2.34-2.08 4.37 1.38 6.67 4.7 3.14 5.96 5.46 6.97 10.3.78 3.7 2.09 4.62 8.75 4.62 5.5 0 7.57 2.57 8.15 7.75.06.5.09.82.17 1.84.25 3.06.55 4.17 1.46 4.72 1.2.74 3.69.13 7.98-2.25a72.09 72.09 0 0 0 8.82-5.8c3.55-2.7 13.34-11.65 15.24-13.07 3.79-2.83 7.18-4.19 11.18-4.19 6.77 0 11.8-3.67 18.83-12l3.45-4.13a60.07 60.07 0 0 1 3.37-3.72 11.72 11.72 0 0 1 5.01-2.91c1.1-.34 1.6-.45 3.97-.89 2.95-.55 4.07-1.02 4.65-2.23.76-1.59.28-4.5-1.74-9.43a84.46 84.46 0 0 1-2.74-7.69c-.31-1.03-1.4-4.8-1.66-5.61-1.95-6.2-4.16-7.39-15.14-7.39-6.5 0-10.61-1.93-14.98-5.98-.44-.4-2.46-2.37-3.01-2.86-3.65-3.3-6.52-3.85-11.79-1.21-12.67 6.33-17.15 10.65-22.78 20.8zm55.86 11.93c-2.98 6.45-16.78 15.26-26.74 15.26-5.33 0-7.56-2.98-7.11-7.86.32-3.48 2.1-7.91 3.93-10.61l1.52-2.32a44.95 44.95 0 0 1 1.88-2.7c3.66-4.8 7.85-7.45 13.62-7.45 9.06 0 15.75 9.52 12.9 15.68zm-.9-.42c2.52-5.47-3.65-14.26-12-14.26-5.4 0-9.33 2.48-12.82 7.06-.6.8-1.17 1.6-1.85 2.64 0 0-1.2 1.87-1.52 2.33-1.74 2.57-3.46 6.85-3.77 10.14-.4 4.33 1.43 6.77 6.12 6.77 9.57 0 23.02-8.58 25.83-14.68zm-69.67 20.74c2.08.18 4.44.81 5.88 1.8 2.12 1.47 2.2 3.6-.26 6.05-5.14 5.15-12.85 4.34-12.85-1.35 0-4.66 3.14-6.84 7.23-6.5zm-.09 1c-3.56-.3-6.14 1.5-6.14 5.5 0 4.58 6.53 5.26 11.15.65 2.03-2.04 1.98-3.43.4-4.52-1.27-.88-3.48-1.47-5.4-1.63zm29.59-225.95c4.64 2.35 17.27 8.24 19.39 9.43a24.14 24.14 0 0 1 7.05 5.64 45.03 45.03 0 0 1 3.75 5.2c2.4 3.78.04 7.66-6.2 11.63-4.97 3.16-12.18 6.3-21.95 9.82-4.84 1.74-19.63 6.68-21.1 7.2-6.59 2.33-14.85.1-25.14-5.86-3.93-2.27-8-5-12.94-8.54-2.23-1.61-9.5-6.99-10.7-7.85a81.21 81.21 0 0 0-8.63-5.7c-4.82-2.6-4.45-6.64.17-12.13 3.27-3.88 4.17-4.67 18.1-16.33a230.2 230.2 0 0 0 8.89-7.74 95.2 95.2 0 0 0 4.72-4.66c5.08-5.43 9.8-6.49 14.97-3.92 2.24 1.1 4.53 2.85 7.43 5.52 1.48 1.37 6.94 6.72 7.98 7.7 5.2 4.91 9.46 8.2 14.2 10.6zm-.46.9c-4.85-2.45-9.18-5.79-14.44-10.76-1.05-1-6.5-6.34-7.97-7.69-2.83-2.61-5.06-4.3-7.2-5.37-4.75-2.36-9-1.4-13.8 3.71a96.18 96.18 0 0 1-4.76 4.71c-2.48 2.3-5.16 4.62-8.92 7.77-13.86 11.6-14.77 12.4-17.98 16.21-4.28 5.08-4.58 8.4-.46 10.61 2.23 1.2 4.9 2.99 8.74 5.77 1.2.87 8.47 6.24 10.7 7.85a154.8 154.8 0 0 0 12.85 8.49c10.06 5.82 18.07 7.98 24.3 5.78 1.48-.52 16.27-5.47 21.1-7.2 9.7-3.5 16.86-6.61 21.75-9.72 5.84-3.71 7.9-7.1 5.9-10.26a44.09 44.09 0 0 0-3.67-5.08 23.16 23.16 0 0 0-6.78-5.42c-2.08-1.16-14.68-7.05-19.36-9.4zm-38.83 8.05c3.11-.37 5.7-.13 8.4.7 2.15.66 2.74.93 8.64 3.77 4.75 2.29 8.39 3.86 13.19 5.56 8.38 2.97 11.32 6.23 8.83 9.76-2.08 2.94-8.04 5.92-17.84 9.18-8.45 2.82-15.48 2.35-21.43-.9-4.65-2.55-8.33-6.5-12.15-12.3-2.9-4.41-2.73-8.2.16-11.06 2.48-2.45 6.87-4.07 12.2-4.7zm.12 1c-5.13.6-9.33 2.16-11.62 4.42-2.53 2.5-2.68 5.77-.02 9.8 3.73 5.68 7.3 9.51 11.8 11.97 5.7 3.11 12.43 3.57 20.62.84 9.59-3.2 15.44-6.12 17.34-8.82 1.94-2.75-.5-5.45-8.35-8.24-4.84-1.72-8.5-3.3-13.28-5.6-5.84-2.81-6.42-3.07-8.5-3.71a18.42 18.42 0 0 0-8-.66zM202.5 500.38c0 4.78-1.45 7.56-4.43 8.93-2.29 1.05-4.55 1.23-10.79 1.2l-1.78-.01c-9.19 0-17-7.65-17-15.5 0-7.59 10.6-10.51 19.74-5.44 2.78 1.55 4.21 1.94 8.57 2.75 4.44.83 5.69 2.27 5.69 8.07zm-1 0c0-5.3-.9-6.34-4.88-7.08-4.45-.83-5.96-1.25-8.86-2.86-8.57-4.76-18.26-2.1-18.26 4.56 0 7.3 7.36 14.5 16 14.5h1.79c6.06.04 8.26-.14 10.36-1.1 2.6-1.2 3.85-3.6 3.85-8.02zm33.33-117.85c3.71-1.31 8.7-2.7 16.1-4.55 2.58-.65 16.53-4.04 20.56-5.05 19.59-4.93 31.55-8.9 38.23-13.35 14.93-9.95 36.87-33.88 43.83-47.8 2.25-4.5 4.65-6.38 7.68-6.25 1.26.06 2.61.45 4.32 1.2a50.81 50.81 0 0 1 3.54 1.7l1.26.63c4.78 2.34 8.38 3.44 12.65 3.44 7.2 0 10.01 3.07 8.35 7.91-1.4 4.06-5.92 8.91-11.1 12.02-8.3 4.98-11.75 17.3-11.75 33.57 0 3.59-1.37 6.28-3.98 8.36-1.98 1.58-4.2 2.6-8.47 4.16l-1.02.37c-4.85 1.75-6.98 2.77-8.68 4.46-5.09 5.1-12.54 7.15-20.35 7.15-1.38 0-2.47.92-3.99 3.1-.29.41-1.32 1.95-1.47 2.18-2.68 3.92-4.93 5.72-8.54 5.72-7.84 0-10.74.93-21.76 6.94-5.18 2.82-8.8 3.58-14.66 3.68-.26 0-.47 0-.92.02-4.82.06-7.12.3-10.51 1.34a73.43 73.43 0 0 0-8.89 3.56c-2.17 1-10.53 5.01-10.23 4.87-7.79 3.7-13.32 5.98-18.9 7.57-12.41 3.55-18.58 2.24-27.42-4.07-2.58-1.85-2.72-4.43-.83-7.62 1.45-2.45 3.9-5.09 8.08-8.97l1.78-1.64c3.92-3.6 4.48-4.11 5.9-5.53 2.32-2.32 3.12-3.5 5.48-7.63 1.93-3.36 3.37-5.11 6.27-7.06 2.3-1.54 5.34-2.98 9.44-4.43zm.34.94c-4.03 1.42-7 2.83-9.22 4.32-2.75 1.85-4.1 3.49-5.96 6.73-2.4 4.2-3.24 5.44-5.64 7.83-1.43 1.44-2 1.96-5.94 5.57l-1.77 1.63c-4.1 3.82-6.52 6.41-7.9 8.75-1.65 2.79-1.54 4.8.55 6.3 8.6 6.14 14.46 7.38 26.57 3.92 5.5-1.57 11-3.84 18.74-7.51-.3.14 8.06-3.88 10.24-4.88a74.3 74.3 0 0 1 9.01-3.6c3.51-1.09 5.89-1.33 10.8-1.4h.91c5.72-.1 9.18-.83 14.2-3.57 11.16-6.08 14.2-7.06 22.24-7.06 3.19 0 5.2-1.6 7.71-5.28l1.48-2.2c1.7-2.43 3-3.52 4.81-3.52 7.57 0 14.78-2 19.65-6.85 1.83-1.84 4.04-2.9 9.04-4.7l1.02-.37c8.6-3.13 11.79-5.67 11.79-11.58 0-16.6 3.53-29.2 12.24-34.43 5-3 9.35-7.67 10.66-11.48 1.42-4.13-.83-6.59-7.4-6.59-4.45 0-8.19-1.14-13.09-3.54-7.52-3.67-6.78-3.34-8.72-3.43-2.58-.1-4.65 1.52-6.74 5.7-7.04 14.07-29.1 38.14-44.17 48.19-6.81 4.54-18.84 8.52-38.55 13.48-4.03 1.02-17.98 4.4-20.56 5.05-7.37 1.84-12.33 3.23-16 4.52zM252 387.5c2.08 0 4-.2 7.25-.69 5.22-.77 6.64-.9 8.46-.5 2.52.56 3.79 2.35 3.79 5.69 0 4.05-2.27 7.29-6.62 10.11-3.24 2.1-6.53 3.53-14.15 6.4l-.27.1-2.28.86c-3.04 1.16-5.27 2.52-9.33 5.43l-.8.57c-8.19 5.88-13.35 8.03-23.05 8.03-4.98 0-6.88-2.03-5.75-5.62.87-2.81 3.58-6.56 7.8-11.13 1.26-1.37 2.64-2.8 4.15-4.3 3.17-3.14 11.25-10.61 11.45-10.8.46-.47.93-.89 1.4-1.26 3.38-2.71 5.77-3.08 14.18-2.93 1.65.03 2.63.04 3.77.04zm0 1c-1.15 0-2.13-.01-3.79-.04-8.18-.14-10.4.2-13.54 2.71-.44.35-.88.74-1.32 1.18-.2.21-8.3 7.69-11.45 10.82a134.6 134.6 0 0 0-4.12 4.26c-4.12 4.47-6.76 8.12-7.58 10.75-.9 2.88.45 4.32 4.8 4.32 9.46 0 14.44-2.07 22.46-7.84l.8-.57c4.13-2.96 6.42-4.36 9.56-5.56l2.3-.86.25-.1c7.55-2.84 10.8-4.25 13.97-6.3 4.08-2.65 6.16-5.6 6.16-9.27 0-2.89-.97-4.26-3-4.7-1.65-.37-3.05-.25-8.1.5-3.3.5-5.26.7-7.4.7zm112.47-45.34c-1.88 5.44-1.98 6.76-.98 12.76 1.18 7.06-1.38 16.58-5.49 16.58a16.89 16.89 0 0 0-1.51.07l-.64.04c-2.86.18-4.83.17-6.94-.17-6.55-1.06-10.41-5.14-10.41-13.44 0-13.9 2.14-19.69 8.13-26.33a21.9 21.9 0 0 0 2.52-3.75c.59-1.03 2.78-5.13 2.72-5.01 4.44-8.14 7.71-11.53 12.25-10.4 1.17.3 2.2.77 3.58 1.59l1.39.84a20 20 0 0 0 3.1 1.6c.7.27 1.8.32 4.75.26l.72-.01c3.16-.05 4.78.08 5.83.66 1.61.89 1.2 2.56-1.14 4.9a215.9 215.9 0 0 1-3.86 3.76c-10.6 10.1-12.75 12.4-14.02 16.05zm-.94-.32c1.34-3.9 3.46-6.17 14.27-16.46 1.55-1.47 2.73-2.62 3.85-3.73 1.94-1.95 2.17-2.88 1.35-3.33-.82-.45-2.37-.58-5.32-.53l-.72.01c-3.14.06-4.26.02-5.14-.34-1.06-.41-1.97-.9-3.25-1.67l-1.38-.83a12.1 12.1 0 0 0-3.31-1.47c-3.88-.97-6.92 2.17-11.13 9.9.07-.13-2.14 3.98-2.73 5.02a22.71 22.71 0 0 1-2.65 3.92c-5.81 6.47-7.87 12-7.87 25.67 0 7.79 3.48 11.47 9.57 12.45 2.01.33 3.92.34 6.71.16a371.33 371.33 0 0 0 1.23-.07c.42-.03.73-.04.99-.04 3.2 0 5.6-8.9 4.5-15.42-1.02-6.16-.91-7.64 1.03-13.24zm-9.26 12.42c.58.52 2.5 1.9 2.55 1.93 1.96 1.57 2.04 3.31.01 6.36-3.74 5.64-8.83 3.09-8.83-4.55 0-3.81.51-5.67 2.07-6.02 1.18-.26 2 .3 4.2 2.28zm-1.34 1.48c-1.5-1.35-2.23-1.85-2.43-1.8-.17.03-.5 1.23-.5 4.06 0 5.87 2.67 7.21 5.17 3.45 1.5-2.26 1.47-2.84.4-3.7.03.03-1.95-1.4-2.64-2zm222.9-130.19c2.2-1.1 3.67-1.66 5.88-2.36l.28-.09a48.92 48.92 0 0 0 8.79-3.55c4.17-2.08 6.35-1.88 6.96.84.44 2 .2 4.01-1.25 12.7-2.27 13.62-9.16 26.14-21.17 36.3-4.3 3.63-7.41 4.39-9.75 2.44-1.88-1.57-3.1-4.57-4.61-10.48-.3-1.15-1.43-5.83-1.72-6.96a114.18 114.18 0 0 0-2.71-9.22c-2.4-6.82-3.03-10.78-2.1-12.94.77-1.83 2.08-2.24 5.6-2.45 1.49-.09 2.09-.14 2.97-.28l1.95-.33c.72-.12 1.22-.2 1.68-.29 1.1-.2 1.92-.38 2.71-.6 1.7-.49 3.42-1.2 6.49-2.73zm.44.9c-3.11 1.54-4.88 2.29-6.65 2.79-.84.23-1.69.42-2.81.63a108.77 108.77 0 0 1-3.81.63c-.77.13-1.39.19-2.92.28-3.13.18-4.17.51-4.74 1.85-.78 1.84-.2 5.62 2.13 12.2a115.12 115.12 0 0 1 2.74 9.31l1.72 6.96c1.46 5.7 2.62 8.58 4.28 9.96 1.87 1.56 4.49.93 8.47-2.44 11.82-10 18.6-22.3 20.83-35.7 1.4-8.45 1.65-10.51 1.25-12.31-.41-1.87-1.86-2-5.54-.16a49.87 49.87 0 0 1-8.93 3.6l-.28.1a35.4 35.4 0 0 0-5.74 2.3zm-4.5 6.58c1.37-.32 2.5-.75 3.9-1.42.35-.18 2.57-1.31 3.32-1.67 1.5-.71 2.97-1.31 4.7-1.89 2.7-.9 4.64-.77 5.88.4.98.94 1.34 2.26 1.41 4.18.02.4.02.7.02 1.37 0 5.63-4.63 16.88-11.34 22.75-4.34 3.8-7.31 4.67-9.92 2.52-2.06-1.7-3.5-4.65-6.67-12.91-1.86-4.83-2.05-8.1-.68-10.2 1.12-1.7 2.9-2.36 5.83-2.7l1.26-.12c1.19-.12 1.75-.19 2.3-.31zm-2.1 2.3l-1.22.12c-2.4.27-3.7.76-4.39 1.81-.93 1.43-.78 4.1.87 8.38 3.02 7.84 4.41 10.71 6.08 12.09 1.63 1.34 3.64.75 7.33-2.48C584.6 250.77 589 240.08 589 235c0-.64 0-.93-.02-1.29-.05-1.44-.3-2.33-.79-2.8-.6-.57-1.8-.65-3.87.04a37.95 37.95 0 0 0-4.47 1.8c-.72.34-2.93 1.47-3.32 1.66a19.54 19.54 0 0 1-4.3 1.56c-.66.16-1.28.24-2.56.36zm-227.73-88.98c-1.59 4.3-3.54 7.25-7.14 11.4l-2.6 2.97a67.02 67.02 0 0 0-2.63 3.23 46.4 46.4 0 0 0-4.68 7.5c-2.85 5.7-7.14 10.18-12.85 13.89-4.25 2.76-8.25 4.62-15.67 7.59-11.01 4.4-16.43 1.26-27.22-16.4-2.86-4.69-8.8-8.63-17.98-12.66-3-1.33-12.88-5.24-14.43-5.92-4.96-2.18-7.04-3.72-6.42-5.85.67-2.32 5.3-4.05 15.48-6.08 16.63-3.32 26.93-3.82 39.93-3.02 7.9.49 9.67.5 12.74-.26 1.99-.48 3.92-1.3 6-2.6l2.79-1.71c9.86-6.14 12.94-7.96 17.3-9.9 6.03-2.71 10.57-3.32 13.94-1.4 7.2 4.12 7.68 7.7 3.44 19.22zm-1.88-.7c3.95-10.7 3.6-13.26-2.56-16.78-2.66-1.52-6.62-.99-12.12 1.48-4.24 1.9-7.3 3.7-17.07 9.77l-2.79 1.73a22.6 22.6 0 0 1-6.57 2.84c-3.36.81-5.22.8-13.34.3-12.84-.78-22.97-.29-39.41 3-4.9.97-8.45 1.88-10.79 2.75-2.03.76-3.04 1.45-3.17 1.91-.16.57 1.48 1.79 5.3 3.46 1.5.67 11.39 4.58 14.44 5.93 9.52 4.19 15.74 8.3 18.87 13.44 10.35 16.93 14.87 19.56 24.78 15.6 7.3-2.93 11.21-4.75 15.33-7.42 5.42-3.53 9.47-7.75 12.15-13.1 1.44-2.9 3.02-5.4 4.86-7.82a68.95 68.95 0 0 1 2.72-3.33l2.6-2.97c3.46-3.99 5.28-6.75 6.77-10.79zm-6.64-.39c-7.94 12.8-18.53 21.75-33.3 25.23-7.82 1.83-12.47-.79-13.12-5.93-.55-4.45 2.29-9.06 6-9.06 3.02 0 5.6-1.68 15.38-9.16 1.47-1.12 2.57-1.96 3.66-2.74 4.4-3.2 7.77-5.17 10.82-6.08 5.57-1.67 9.33-2.15 11.35-1.22 2.5 1.14 2.22 4.13-.79 8.96zm-.84-.52c2.72-4.4 2.94-6.74 1.21-7.53-1.71-.79-5.32-.33-10.65 1.27-2.9.87-6.2 2.79-10.51 5.92-1.08.79-2.18 1.62-3.65 2.74-10.08 7.72-12.62 9.36-15.98 9.36-3.02 0-5.5 4.02-5 7.94.56 4.5 4.62 6.78 11.89 5.07 14.48-3.4 24.86-12.18 32.69-24.77zM461.17 33.53c13.88 4.96 20.75 4.96 31.62.01 3.02-1.37 5.47-2.94 11-6.82 5.57-3.92 8.05-5.51 11.14-6.92 4.14-1.88 7.78-2.38 11.22-1.28 3.92 1.26 6.2 12.3 6.78 28.45.5 14.2-.52 28.93-2.46 34.2-1.82 4.93-5.86 8.17-11.51 10.02A41.7 41.7 0 0 1 506 93.01c-5.79 0-9 2.4-12.2 7.64-.37.59-1.55 2.6-1.71 2.87-1.75 2.9-3.05 4.33-4.93 4.95-.94.32-2.07.83-3.87 1.74l-2.43 1.23c-1.03.53-1.87.94-2.7 1.34-6.43 3.1-11.73 4.72-17.16 4.72-5.71 0-10.04 2.09-14.02 5.92-1.16 1.11-4.2 4.53-4.63 4.94-2.54 2.44-5.93 4.24-10.85 6.1-1.4.52-5.98 2.13-6.25 2.22l-2.06.78c-.89.36-1.78.63-2.7.81-5.55 1.14-11.14-.54-17.98-4.42-1.27-.73-5.13-3.06-5.76-3.42-2.05-1.16-4.12-1.53-9.09-1.9l-1.73-.15c-4.78-.4-7.68-1.14-10.22-2.97-5-3.61-6.77-7.76-5.65-12.33 1.33-5.42 6.5-11.02 14.85-17.28a169.2 169.2 0 0 1 6.5-4.61c-.33.23 4.33-2.92 5.3-3.6 2.73-1.91 4.8-3.9 12.75-12.04l1.09-1.1c3.49-3.56 5.89-5.89 8.12-7.83 2.9-2.5 4.72-5.95 7.5-13.05l.63-1.61c2.7-6.92 4.28-10 6.87-12.33 1.42-1.28 6.68-6.54 7.93-7.5 3.98-3 8.01-2.73 19.57 1.4zm-.34.94c-11.26-4.02-15-4.28-18.62-1.53-1.19.9-6.4 6.11-7.88 7.43-2.42 2.18-3.96 5.19-6.6 11.95l-.63 1.61c-2.83 7.26-4.72 10.8-7.77 13.45a141.85 141.85 0 0 0-9.16 8.87c-8.02 8.2-10.08 10.2-12.88 12.16-.99.69-5.65 3.84-5.31 3.6-2.5 1.71-4.52 3.13-6.47 4.59-8.17 6.13-13.23 11.6-14.48 16.72-1.02 4.15.58 7.9 5.26 11.27 2.36 1.7 5.11 2.4 9.72 2.8l1.73.13c5.12.4 7.28.78 9.5 2.05.65.36 4.5 2.7 5.76 3.4 6.66 3.78 12.04 5.4 17.29 4.32.86-.17 1.7-.42 2.52-.75a67 67 0 0 1 2.1-.8c.28-.1 4.86-1.7 6.24-2.22 4.8-1.8 8.08-3.56 10.5-5.88.4-.38 3.44-3.8 4.63-4.94 4.16-4 8.72-6.2 14.72-6.2 5.25 0 10.42-1.59 16.73-4.62.82-.4 1.65-.8 2.68-1.33.12-.06 1.93-.99 2.43-1.23 1.84-.93 3-1.46 4-1.8 1.6-.52 2.76-1.82 4.39-4.52l1.7-2.88c3.39-5.5 6.87-8.11 13.07-8.11 4.45 0 8.73-.49 12.64-1.77 5.4-1.76 9.2-4.8 10.9-9.41 1.87-5.11 2.9-19.75 2.39-33.83-.56-15.53-2.81-26.48-6.08-27.52-3.18-1.02-6.57-.55-10.5 1.23-3.02 1.37-5.47 2.94-11 6.83-5.57 3.92-8.05 5.5-11.14 6.92-11.13 5.05-18.26 5.05-32.38.01zM475 55c5.38 0 7.55-.21 9.72-.96 1.26-.43 9.95-4.8 14.88-6.96 1.9-.82 3.56-2.44 6.6-6.04 2.56-3.04 3.19-3.75 4.4-4.84 3.7-3.35 7.07-3.28 10.22 1.23 6.23 8.9 5.61 15.94.07 27.02a71.26 71.26 0 0 0-2.5 5.48c-.32.8-1 2.7-1.09 2.9-.17.45-.34.81-.54 1.17-.63 1.14-1.56 2.21-4.05 4.7-2.4 2.4-5.16 3.27-11.68 4.33-1.81.3-2.2.36-3 .51-6.02 1.1-9.6 2.69-12.24 6.07-3.57 4.59-7.9 7.48-14.98 10.74-.55.24-1.1.5-1.8.8l-1.78.8a60.08 60.08 0 0 0-7.7 3.9c-2.57 1.6-4.79 2.35-9.42 3.46-8.58 2.06-12.28 3.76-17.37 9.36-5.12 5.64-10.17 7.64-16.63 6.7-5.36-.79-10.63-3.01-23.56-9.48-6.3-3.15-6.43-7.78-1.5-13.56 3.38-3.94 3.52-4.06 19.4-16.44 8.12-6.33 12.97-10.57 16.63-14.88 2.53-2.98 4.2-5.73 4.96-8.3 5.5-18.3 12.5-21.98 22.78-15.56 1.95 1.22 6.61 4.55 7.18 4.9 3.36 2.15 6.52 2.95 13 2.95zm0 2c-6.84 0-10.37-.89-14.08-3.26-.63-.4-5.27-3.71-7.16-4.9-9.05-5.65-14.66-2.7-19.8 14.45-.86 2.87-2.67 5.85-5.35 9.01-3.78 4.45-8.7 8.75-16.94 15.17-15.66 12.21-15.86 12.38-19.1 16.16-4.17 4.9-4.09 8 .88 10.48 12.71 6.35 17.89 8.54 22.94 9.28 5.78.84 10.18-.9 14.87-6.06 5.42-5.96 9.45-7.82 18.38-9.96 4.43-1.07 6.5-1.76 8.83-3.22a61.7 61.7 0 0 1 7.94-4.02l1.78-.8 1.78-.8c6.82-3.13 10.91-5.87 14.24-10.14 3-3.87 7-5.64 13.46-6.82.83-.15 1.21-.21 3.04-.51 6.1-1 8.6-1.78 10.58-3.77 2.36-2.36 3.21-3.34 3.72-4.26.15-.27.29-.56.44-.94.06-.15.75-2.06 1.09-2.9.64-1.6 1.45-3.4 2.57-5.64 5.24-10.49 5.8-16.8.07-24.98-2.4-3.44-4.37-3.48-7.24-.89-1.11 1-1.73 1.7-4.22 4.65-3.24 3.85-5.04 5.59-7.32 6.59-4.82 2.1-13.62 6.53-15.03 7.01-2.44.84-4.79 1.07-10.37 1.07zm-12.7 8.6c5.47 3.9 10.34 3.72 18.23.88 5.39-1.94 5.92-2.1 7.7-2.1 2.5-.01 4.21 1.36 5.24 4.46 1.66 4.98-2.32 8.52-12.3 12.68-2.7 1.13-16.25 6.18-20 7.73-7.86 3.24-13.93 6.42-18.87 10.15-13.02 9.84-18.36 11.93-23.71 9.68a24.67 24.67 0 0 1-3.62-1.98l-1.99-1.28a90.4 90.4 0 0 0-2.24-1.4c-3.33-2-2.82-4.28.85-7.34 1.35-1.13 10.66-7.61 13.53-9.91 7.1-5.69 11.91-11.47 14.41-18.34 3.07-8.45 4.89-12.1 6.8-13.39 1.73-1.16 3.36-.53 6.18 1.9.63.56 3.4 3.08 4.11 3.7 1.93 1.7 3.71 3.15 5.67 4.55zm-.6.8c-1.98-1.42-3.79-2.88-5.74-4.6-.73-.64-3.48-3.16-4.1-3.7-2.5-2.16-3.75-2.65-4.97-1.83-1.66 1.11-3.44 4.7-6.42 12.9-2.57 7.07-7.5 12.99-14.72 18.78-2.91 2.33-12.21 8.8-13.52 9.9-3.22 2.68-3.56 4.17-.97 5.72l2.26 1.4 1.99 1.28c1.47.93 2.48 1.5 3.47 1.91 4.9 2.07 9.96.07 22.72-9.56 5.02-3.79 11.15-7 19.1-10.28 3.76-1.55 17.3-6.6 20-7.72 9.5-3.97 13.14-7.2 11.73-11.44-.9-2.71-2.25-3.8-4.3-3.79-1.6 0-2.15.17-7.36 2.05-8.17 2.94-13.34 3.14-19.16-1.01z'%3E%3C/path%3E%3C/svg%3E"); + background-color: #a9a9a9; +} +img, iframe { + border-radius: 5px; } a { color: #333; @@ -31,9 +43,24 @@ a:not(.btn):visited, a:not(.btn):active, a:not([href]):not([tabindex]), a:not([href]):not([tabindex]):focus { - text-decoration: none ! important; + text-decoration: none ! important; +} +#full-page-loader { + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + background-color: rgba(0,0,0,0.6); + display: flex; + justify-content: center; + align-items: center; + z-index: 99995; + overflow: hidden; +} +#full-page-loader i { + font-size: 150px; } - table.list a:not(.btn), body.admin form .input a:not(.btn), body.admin.modal-open .modal a:not(.btn):not(.upload-button), @@ -56,7 +83,11 @@ body.admin h2 span a:hover, border-color: transparent; } a:not(.btn):not(.btn-arrow):not(.swiper-button-prev):not(.swiper-button-next):hover { - text-decoration: underline ! important; + text-decoration: underline ! important; +} +.swiper-button-next, +.swiper-button-prev { + border-radius: 50%; } ul.menu:not(#user-menu):not(#footer-menu) a:hover { text-decoration: none ! important; @@ -87,8 +118,8 @@ button, } .btn-outline-light { color: #333; - background-color: #fff; - border-color: #ccc; + background-color: #fff; + border-color: #ccc; } .btn-outline-light.disabled { color: #333; @@ -117,13 +148,13 @@ button, .btn-danger:active, .btn-danger:active:focus, .btn-danger.disabled { - background-color: #f3515c; - border-color: #f3515c; - color: #fff; + background-color: #f3515c; + border-color: #f3515c; + color: #fff; } .btn-danger { - background-color: #f3515c; - border-color: #d9534f; + background-color: #f3515c; + border-color: #d9534f; } .btn-warning { background-color: #f0ad4e; @@ -141,7 +172,7 @@ button, .btn-success:hover, .btn-danger:hover, .btn-warning:hover { - opacity: 0.8; + opacity: 0.8; } .sc { clear: both; height: 0px; font-size: 0px; background-color: transparent; } .hide { @@ -293,7 +324,6 @@ h2.info a:not(.btn) { width: 145px; background: #efefef; margin-right: 5px; - border-top: 1px solid #dfdfdf; } .vertical.menu a, .vertical.menu > li img.logo { @@ -352,35 +382,39 @@ a.toggle-link.collapsed i:before { font-family: 'Open Sans'; } #scroll-to-top { - position: fixed; - bottom: 10px; - left: 10px; - z-index: 99; - display: none; + position: fixed; + bottom: 10px; + left: 10px; + z-index: 99; + display: none; } #scroll-to-top a { - width: 45px; - height: 45px; - display: block; - -webkit-transition: 0.6s; - -moz-transition: 0.6s; - -o-transition: 0.6s; - transition: 0.6s; - background: #fff; - border-radius: 100px; - font-size: 34px; - text-align: center; - background: #fff; - line-height: 1.5; + width: 45px; + height: 45px; + display: block; + -webkit-transition: 0.6s; + -moz-transition: 0.6s; + -o-transition: 0.6s; + transition: 0.6s; + background: #fff; + border-radius: 50%; + font-size: 34px; + text-align: center; + background: #fff; + line-height: 1.5; } #scroll-to-top a i { - font-size: 41px; + font-size: 43px; +} +div.input.error input { + border-bottom-right-radius: 0 ! important; + border-bottom-left-radius: 0 ! important; } .truncate { - width: 250px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + width: 250px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } input[type="text"], @@ -399,6 +433,8 @@ form div.error-message { padding: 3px 5px; margin-bottom: 5px; width: 270px; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; } form div.error-message.long { width: 562px; @@ -503,7 +539,7 @@ i.gold { i.fa.not-ok, i.fas.not-ok, i.far.not-ok { - color: red; + color: var(--not-ok-red); } i.fa.neutral, i.fas.neutral, @@ -591,28 +627,29 @@ tr.selected > td > div > i.far, table:not(.no-hover) tr:hover > td > div > i.far top: 8px; font-size: 15px; } -.ui-dialog-content #flashMessage { - position: absolute; - top: 10px; - width: 98%; - left: 1% ! important; -} - -.cookieConsentWrapper button[type="button"].cookieConsent__Button { +.cookieConsentWrapper button.cookieConsent__Button { display: none; } +.cookieConsentWrapper button.cookieConsent__Button:last-child { + display: block; +} .cookieConsentWrapper { - box-shadow: 0px -3px 3px 0px rgba(0, 0, 0, 0.15); + box-shadow: none; + border-radius: 3px; width: 330px; bottom: 5px ! important; left: 5px ! important; + background-color: #fff ! important; + color: #333 ! important; + border-width: 1px; + border-style: solid; } .cookieConsentWrapper .cookieConsent { flex-direction: column; } .cookieConsentWrapper .cookieConsent__Left a { - color: #fff ! important; text-decoration: underline ! important; + color: var(--theme-color); } .cookieConsentWrapper .cookieConsent__Right { margin-top: 15px; @@ -622,9 +659,13 @@ tr.selected > td > div > i.far, table:not(.no-hover) tr:hover > td > div > i.far margin-left: 0; margin-bottom: 0; font-size: 14px; + color: #fff; } .fa-spin { animation:10s infinite, fa-spin 2s infinite linear; -moz-animation:10s infinite, fa-spin 2s infinite linear; /* Firefox */ -webkit-animation:10s infinite, fa-spin 2s infinite linear; /* Safari and Chrome */ } +.calculator-input { + display: none; +} \ No newline at end of file diff --git a/webroot/css/mini-upload-form.css b/webroot/css/mini-upload-form.css index 54ef2cf1d6..b25674699a 100644 --- a/webroot/css/mini-upload-form.css +++ b/webroot/css/mini-upload-form.css @@ -121,5 +121,5 @@ } .upload ul li.error p{ - color:red; + color:var(--not-ok-red); } diff --git a/webroot/css/mobile-dark-mode.css b/webroot/css/mobile-dark-mode.css new file mode 100644 index 0000000000..8fc96c0618 --- /dev/null +++ b/webroot/css/mobile-dark-mode.css @@ -0,0 +1,36 @@ +/** + * FoodCoopShop - The open source software for your foodcoop + * + * Licensed under the GNU Affero General Public License version 3 + * For full copyright and license information, please see LICENSE + * Redistributions of files must retain the above copyright notice. + * + * @since FoodCoopShop 3.6.0 + * @license https://opensource.org/licenses/AGPL-3.0 + * @author Mario Rothauer + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ +@media only screen and (max-device-width: 850px) { + + body.dark #responsive-header, + body.dark .sb-slidebar, + body.dark.admin .sb-toggle-left { + background-color: var(--dark-mode-main-bg-color) ! important; + } + body.dark .sb-slidebar li a, + body.dark #responsive-header a, + body.dark.admin .sb-toggle-left { + color: var(--dark-mode-main-font-color) ! important; + } + body.dark .sb-slidebar li a.active, + body.dark .sb-slidebar li a:hover { + color: var(--dark-mode-2nd-font-color) ! important; + } + body.dark .sb-left a, + body.dark .sb-left li.header, + body.dark .sb-left li.footer { + border-color: var(--dark-mode-main-border-color); + } + +} \ No newline at end of file diff --git a/webroot/css/mobile-frontend-portrait.css b/webroot/css/mobile-frontend-portrait.css index 5ce5293d96..07932610f9 100644 --- a/webroot/css/mobile-frontend-portrait.css +++ b/webroot/css/mobile-frontend-portrait.css @@ -18,10 +18,6 @@ #RegistrationForm .input.checkbox input[type="checkbox"] { margin-top: 8px; } - .ui-dialog { - width: 98% ! important; - max-width: 450px ! important; - } body.categories .blog-post-wrapper, body.manufacturers.detail .blog-post-wrapper, body.pages.home .blog-post-wrapper, @@ -40,9 +36,6 @@ body.blog_posts.detail .blog-post-wrapper { height: 234px; } - .blog-post-wrapper img.blog-post-image { - height: 100px; - } .blog-post-wrapper span.date { display: none; } diff --git a/webroot/css/mobile-frontend.css b/webroot/css/mobile-frontend.css index 5976137db5..43f0006180 100644 --- a/webroot/css/mobile-frontend.css +++ b/webroot/css/mobile-frontend.css @@ -25,12 +25,15 @@ body.customers.new_password_request form, #login-form .input, body.customers.new_password_request .input, - a.blog-post-wrapper img, #RegistrationForm .detail-form, #RegistrationForm .input.checkbox label, #inner-content iframe { width: 100% ! important; } + .color-mode-toggle { + margin-top: 0; + float: none; + } .cookieConsentWrapper { left: 2% ! important; bottom: 1% ! important; @@ -182,8 +185,8 @@ width: 100%; } .pw .amount-wrapper .fas { - font-size: 26px; - margin-top: 0px; + font-size: 18px; + margin-top: 5px; } .manufacturer-wrapper div.c3 a.btn { margin-top: 35px ! important; @@ -211,7 +214,7 @@ height: 55px; } #responsive-header .logo { - height: 50px ! important; + height: 51px ! important; float: left ! important; } #responsive-header .fa-spin, @@ -221,7 +224,7 @@ } #responsive-header .sb-toggle-left { float: left ! important; - margin: 13px 5px 5px 3px ! important; + margin: 12px 8px 5px 3px ! important; text-align: center; width: 25px; } diff --git a/webroot/css/mobile-global.css b/webroot/css/mobile-global.css index 137fcecfb0..fcbc8eb0c9 100644 --- a/webroot/css/mobile-global.css +++ b/webroot/css/mobile-global.css @@ -13,6 +13,39 @@ */ @media only screen and (max-device-width: 850px) { + #responsive-header a:not(.btn-camera), + body.self_services #responsive-header i.fa-circle-notch, + :not(button)> i.fas + :not(.fa-arrow-cycle-right) + :not(.fa-arrow-cycle-left) + :not(.fa-star) + :not(.fa-circle-notch) + :not(.fa-tags) + :not(.fa-shopping-bag) + :not(.fa-minus-circle) + :not(.fa-plus-circle) { + color: var(--theme-color) ! important; + } + body.self_services #responsive-header a.btn:not(.btn-flash-message), + body.self_services #responsive-header a.btn:not(.btn-flash-message) i.ok { + color: #fff ! important; + } + body:not(.admin) .sb-right h3 { + background-color: var(--theme-color) ! important; + } + .sb-left li.header, + .sb-left a:hover, + .sb-left a.active, + .sb-left a:hover i.far, + .sb-left a:hover i.fas:not(.gold), + .sb-left a.active i.fas:not(.gold):not(.fa-pencil-alt) { + background-color: var(--theme-color); + color: #fff ! important; + } + .sb-right .inner { + border-color: var(--theme-color) ! important; + } + /* START chrome pull refresh fix */ html { overflow-y: scroll; @@ -35,8 +68,8 @@ text-decoration: none ! important; } #container { - overflow: hidden ! important; /* avoids iphone scrolling bug */ min-height: initial ! important; + overflow: hidden ! important; /* avoids iphone scrolling bug */ } #responsive-header .responsive-cart { float: right ! important; @@ -67,9 +100,6 @@ #flashMessage img { height: 100px; } - .ui-dialog-content #flashMessage { - left: 50% ! important; - } .sb-left { -webkit-box-shadow: 5px 0 5px -2px #888; box-shadow: 5px 0 5px -2px #888; @@ -169,5 +199,4 @@ .modal-dialog .block hr:last-of-type { display: block; } - } \ No newline at end of file diff --git a/webroot/css/mobile-self-service.css b/webroot/css/mobile-self-service.css index 3842e91df9..b757f49304 100644 --- a/webroot/css/mobile-self-service.css +++ b/webroot/css/mobile-self-service.css @@ -14,10 +14,12 @@ @media only screen and (max-device-width: 850px) { .self-service, .self-service .header, - .self-service #products, - .self-service .pw .quantity-in-units-input-field-wrapper input { + .self-service #products { width: 100%; } + .self-service .pw .quantity-in-units-input-field-wrapper input { + width: 75% ! important; + } body.customers.login #content.self-service { min-height: 100vh; } diff --git a/webroot/css/modal.css b/webroot/css/modal.css index ce44bc731a..5a94767c92 100644 --- a/webroot/css/modal.css +++ b/webroot/css/modal.css @@ -21,6 +21,7 @@ .modal-header { color: #fff; min-height: 55px; + border-bottom: none; } .modal-header .btn-close { background-image: none; @@ -184,7 +185,7 @@ body.iframe_self_service_order { text-align: right; } #order-detail-product-price-edit-form .price-per-unit-info-text { - color:red; + color:var(--not-ok-red); float:left; margin-bottom:5px; } @@ -275,7 +276,8 @@ body.iframe_self_service_order { #product-categories-edit-form input[type="checkbox"], #order-detail-product-quantity-edit-form input[type="checkbox"], #order-detail-pickup-day-edit input[type="checkbox"], -#order-detail-product-price-edit-form input[type="checkbox"] { +#order-detail-product-price-edit-form input[type="checkbox"], +#order-detail-customer-edit-form input[type="checkbox"] { float: left; } #modal-product-quantity-edit input[type="number"], diff --git a/webroot/css/self-service.css b/webroot/css/self-service.css index 86f76f5a96..4a4bb7692a 100644 --- a/webroot/css/self-service.css +++ b/webroot/css/self-service.css @@ -18,10 +18,10 @@ body.self_services { body.self_services #content, .self-service, .self-service .footer { - width: 917px; + width: 918px; } body.self_services #content { - margin-top: 104px; + margin-top: -46px; } .self-service { background-color: #fff; @@ -33,7 +33,7 @@ body.self_services #content { .self-service .right-box { position: fixed; left: 611px; - width: 305px; + width: 306px; top: 0; overflow-y: auto; background-color: #fff; @@ -74,7 +74,6 @@ body.self_services #content { min-height: calc(100vh - 100px - 66px - 2px); } .self-service .pw { - background: #fff; border-right: 1px solid #d6d4d4; } .self-service .pw div.c1 { @@ -95,9 +94,6 @@ body.self_services #content { left: 315px; width: 396px; } -.self-service #login-form { - padding-top: 70px; -} .self-service #login-form form { padding-top: 20px; position: relative; @@ -187,7 +183,8 @@ body.customers.login .self-service .footer { border: none; } body.customers.login .self-service { - margin-top: 0 ! important; + margin-top: -100px ! important; + min-height: 565px ! important; } body.customers.login .footer .left-wrapper, body.customers.login .footer .right-wrapper .btn-add-deposit { @@ -280,9 +277,13 @@ body.customers.login .footer .right-wrapper .btn-add-deposit { color: #fff; } .self-service .pw .quantity-in-units-input-field-wrapper input { - width: 50px; + width: 72px; text-align: right; } +.self-service .pw .quantity-in-units-input-field-wrapper .calculator-input { + width: 100%; + margin-top: 5px; +} .self-service .pw .amount-wrapper .fas { font-size: 30px; margin-top: -1px; diff --git a/webroot/css/table.css b/webroot/css/table.css index d7f2b19bb5..2e6d886479 100644 --- a/webroot/css/table.css +++ b/webroot/css/table.css @@ -66,13 +66,16 @@ table.list tr.sub-row td { } table.list td.not-available, table.list td.not-available i.sold-out-limit-for-dialog i { - background-color: red; + background-color: var(--not-ok-red); color: white; } +table.list td.image { + background-color: #fff; +} table.list td.negative, b.negative, span.negative { - color: red ! important; + color: var(--not-ok-red) ! important; } table.list tr:hover td.negative, tr:hover b.negative { @@ -83,6 +86,12 @@ table.list td.slim { } table.list th { font-weight: 600; + position: sticky; + position: -webkit-sticky; + top: 44px; /* overriden in admin.initStickyTableHeader */ + z-index: 2; + background-color: #ebebeb; + box-shadow: inset 0 0px 0 #dfdfdf, inset 0 -1px 0 #dfdfdf; /* borders do not work on sticky elements */ } table.list th, table.list td { @@ -96,10 +105,6 @@ table.list tr.fake-th td { padding: 5px 2px; word-break: break-word; } -table.list th { - background-color: #ebebeb; - border-top: 1px solid #dfdfdf; -} table.list th, table.list tr.fake-th { line-height: 14px; @@ -121,6 +126,7 @@ table.list a.btn { .sort a.desc:before { font-family: 'Font Awesome 6 Free'; padding-right: 5px; + font-weight: 600; } .sort a.asc:before { content: "\f0aa"; diff --git a/webroot/css/theme-color.css b/webroot/css/theme-color.css new file mode 100644 index 0000000000..1c1984d2f7 --- /dev/null +++ b/webroot/css/theme-color.css @@ -0,0 +1,117 @@ +/** + * FoodCoopShop - The open source software for your foodcoop + * + * Licensed under the GNU Affero General Public License version 3 + * For full copyright and license information, please see LICENSE + * Redistributions of files must retain the above copyright notice. + * + * @since FoodCoopShop 3.6.0 + * @license https://opensource.org/licenses/AGPL-3.0 + * @author Mario Rothauer + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +#header .logo-wrapper { + width: var(--logo-width); +} +#header .logo { + max-width: var(--logo-width); +} +#main-menu { + width: calc(100% - var(--logo-width) - 34px); +} +#header .logo { + max-height: var(--logo-max-height); +} + +::selection { + background: var(--theme-color); + color: #fff; +} + +h2.info::selection, +h2.info b::selection, +#flashMessage.success::selection, +#flashMessage.success b::selection { + background-color: #fff; + color: #000; +} + +.box h3, +.btn-success, +.vertical.menu a:hover, .vertical.menu a:hover i, .vertical.menu a.active, .vertical.menu a.active i, +.vertical.menu li.heading, +.menu.vertical a:hover span.additional-info, +.menu.vertical a.active span.additional-info, +#main-menu a::after, +#categories-menu li.header, +#manufacturers-menu li.header, +h2.info, +#flashMessage.success, +.modal-header, +.cookieConsentWrapper .cookieConsent__Right button, +.drop a.upload-button, +#footer .bottom { + background-color: var(--theme-color); +} + +h1, +h2, +a.blog-post-wrapper h3, +.pw .price, +#scroll-to-top a, +#scroll-to-top a i, +.vertical.menu a i.fas, +.vertical.menu span.additional-info, +a:not(.btn), a:not(.btn):visited, a:not(.btn):active, +a.btn.edit-shortcut-button, +a.btn.prev-button i, a.btn.next-button i, +.pw .price-asterisk, +i.fa.ok, i.fas.ok, i.far.ok, +body.carts .cart:not(#cart) span.amount .btn, +#full-page-loader i, +.btn-cart i.fa-cart-plus, +.btn-cart i.fa-shopping-bag { + color: var(--theme-color); +} + +.blog-wrapper .swiper-button-prev, +.blog-wrapper .swiper-button-next, +body.blog_posts.detail #inner-content h2.further-news, +body.customers.registration_successful #inner-content h2.further-news, +a.btn-arrow:hover { + color: var(--theme-color) ! important; +} + +.btn-success, +.btn-success:active:hover, +#flashMessage.success, +hr, +.pw, .manufacturer-wrapper, +.cookieConsentWrapper, +body.manufacturers.detail #inner-content h2, +h1.middle-line span.middle { + border-color: var(--theme-color); +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.disabled { + background-color: var(--theme-color) ! important; + border-color: var(--theme-color); +} +.btn-success:focus:active, +#flashMessage.success .progress-bar.bg-success, +.bootstrap-select .dropdown-item.active, +.bootstrap-select .dropdown-item:active, +table.list tr.selected { + background-color: var(--theme-color) ! important; +} +.vertical.menu a { + color: #333333; +} +body.customers.login #self-service { + box-shadow: inset 0 0 3em var(--theme-color); +} \ No newline at end of file diff --git a/webroot/files/images/sliders/demo-slider.jpg b/webroot/files/images/sliders/demo-slider.jpg index cad078bd41..043acf64e5 100644 Binary files a/webroot/files/images/sliders/demo-slider.jpg and b/webroot/files/images/sliders/demo-slider.jpg differ diff --git a/webroot/img/bg-v3.5.jpg b/webroot/img/bg-v3.5.jpg deleted file mode 100644 index a5d3a189d9..0000000000 Binary files a/webroot/img/bg-v3.5.jpg and /dev/null differ diff --git a/webroot/img/bg-v3.6.jpg b/webroot/img/bg-v3.6.jpg new file mode 100644 index 0000000000..e20478ec0e Binary files /dev/null and b/webroot/img/bg-v3.6.jpg differ diff --git a/webroot/js/background-image.js b/webroot/js/background-image.js new file mode 100644 index 0000000000..4bdbdc0542 --- /dev/null +++ b/webroot/js/background-image.js @@ -0,0 +1,30 @@ +/** + * FoodCoopShop - The open source software for your foodcoop + * + * Licensed under the GNU Affero General Public License version 3 + * For full copyright and license information, please see LICENSE + * Redistributions of files must retain the above copyright notice. + * + * @since FoodCoopShop 3.6.0 + * @license https://opensource.org/licenses/AGPL-3.0 + * @author Mario Rothauer + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ +foodcoopshop.BackgroundImage = { + + getBackgroundImage: function(theme) { + + var opacity = 0.33; + + if (theme == 'dark') { + opacity = 0.03; + } + + var backgroundImage = 'data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'600\' height=\'600\' viewBox=\'0 0 600 600\'%3E%3Cpath fill=\'%23cccccc\' fill-opacity=\'' + opacity + '\' d=\'M600 325.1v-1.17c-6.5 3.83-13.06 7.64-14.68 8.64-10.6 6.56-18.57 12.56-24.68 19.09-5.58 5.95-12.44 10.06-22.42 14.15-1.45.6-2.96 1.2-4.83 1.9l-4.75 1.82c-9.78 3.75-14.8 6.27-18.98 10.1-4.23 3.88-9.65 6.6-16.77 8.84-1.95.6-3.99 1.17-6.47 1.8l-6.14 1.53c-5.29 1.35-8.3 2.37-10.54 3.78-3.08 1.92-6.63 3.26-12.74 5.03a384.1 384.1 0 0 1-4.82 1.36c-2.04.58-3.6 1.04-5.17 1.52a110.03 110.03 0 0 0-11.2 4.05c-2.7 1.15-5.5 3.93-8.78 8.4a157.68 157.68 0 0 0-6.15 9.2c-5.75 9.07-7.58 11.74-10.24 14.51a50.97 50.97 0 0 1-4.6 4.22c-2.33 1.9-10.39 7.54-11.81 8.74a14.68 14.68 0 0 0-3.67 4.15c-1.24 2.3-1.9 4.57-2.78 8.87-2.17 10.61-3.52 14.81-8.2 22.1-4.07 6.33-6.8 9.88-9.83 12.99-.47.48-.95.96-1.5 1.48l-3.75 3.56c-1.67 1.6-3.18 3.12-4.86 4.9a42.44 42.44 0 0 0-9.89 16.94c-2.5 8.13-2.72 15.47-1.76 27.22.47 5.82.51 6.36.51 8.18 0 10.51.12 17.53.63 25.78.24 4.05.56 7.8.97 11.22h.9c-1.13-9.58-1.5-21.83-1.5-37 0-1.86-.04-2.4-.52-8.26-.94-11.63-.72-18.87 1.73-26.85a41.44 41.44 0 0 1 9.65-16.55c1.67-1.76 3.18-3.27 4.83-4.85.63-.6 3.13-2.96 3.75-3.57a71.6 71.6 0 0 0 1.52-1.5c3.09-3.16 5.86-6.76 9.96-13.15 4.77-7.42 6.15-11.71 8.34-22.44.86-4.21 1.5-6.4 2.68-8.6.68-1.25 1.79-2.48 3.43-3.86 1.38-1.15 9.43-6.8 11.8-8.72 1.71-1.4 3.26-2.81 4.7-4.3 2.72-2.85 4.56-5.54 10.36-14.67a156.9 156.9 0 0 1 6.1-9.15c3.2-4.33 5.9-7.01 8.37-8.07 3.5-1.5 7.06-2.77 11.1-4.02a233.84 233.84 0 0 1 7.6-2.2l2.38-.67c6.19-1.79 9.81-3.16 12.98-5.15 2.14-1.33 5.08-2.33 10.27-3.65l6.14-1.53c2.5-.63 4.55-1.2 6.52-1.82 7.24-2.27 12.79-5.06 17.15-9.05 4.05-3.72 9-6.2 18.66-9.9l4.75-1.82c1.87-.72 3.39-1.31 4.85-1.91 10.1-4.15 17.07-8.32 22.76-14.4 6.05-6.45 13.95-12.4 24.49-18.92 1.56-.96 7.82-4.6 14.15-8.33v-64.58c-4 8.15-8.52 14.85-12.7 17.9-2.51 1.82-5.38 4.02-9.04 6.92a1063.87 1063.87 0 0 0-6.23 4.98l-1.27 1.02a2309.25 2309.25 0 0 1-4.87 3.9c-7.55 6-12.9 10.05-17.61 13.19-3.1 2.06-3.86 2.78-8.06 7.13-5.84 6.07-11.72 8.62-29.15 10.95-11.3 1.5-20.04 4.91-30.75 11.07-1.65.94-7.27 4.27-6.97 4.1-2.7 1.58-4.69 2.69-6.64 3.66-5.63 2.8-10.47 4.17-15.71 4.17-17.13 0-41.44 11.51-51.63 22.83-12.05 13.4-31.42 27.7-45.25 31.16-7.4 1.85-11.85 7.05-14.04 14.69-1.26 4.4-1.58 8.28-1.58 13.82 0 .82.01.98.24 3.63.45 5.18.35 8.72-.77 13.26-1.53 6.2-4.89 12.6-10.59 19.43-13.87 16.65-22.88 46.58-22.88 71.68 0 2.39.02 4.26.06 8.75.12 10.8.1 15.8-.22 21.95-.56 11.18-2.09 20.73-5 29.3h-1.05c2.94-8.56 4.49-18.12 5.05-29.35.31-6.13.34-11.1.22-21.9-.04-4.48-.06-6.36-.06-8.75 0-25.32 9.07-55.47 23.12-72.32 5.6-6.72 8.88-12.99 10.38-19.03 1.09-4.4 1.18-7.85.74-12.93-.23-2.7-.24-2.86-.24-3.72 0-5.62.32-9.57 1.62-14.1 2.28-7.95 6.97-13.44 14.76-15.39 13.6-3.4 32.82-17.59 44.75-30.84C409 360.14 433.58 348.5 451 348.5c5.07 0 9.77-1.33 15.26-4.07 1.93-.96 3.9-2.05 6.58-3.62-.3.18 5.33-3.16 6.98-4.11 10.82-6.21 19.66-9.67 31.11-11.2 17.23-2.3 22.9-4.75 28.57-10.64 4.25-4.41 5.04-5.16 8.22-7.28 4.68-3.11 10.01-7.14 17.55-13.14a1113.33 1113.33 0 0 0 4.86-3.89l1.28-1.02a4668.54 4668.54 0 0 1 6.23-4.98c3.67-2.9 6.55-5.12 9.07-6.95 4.37-3.19 9.16-10.56 13.29-19.4v66.9zm0-116.23c-.62.01-1.27.06-1.95.13-6.13.63-13.83 3.45-21.83 7.45-3.64 1.82-8.46 2.67-14.17 2.71-4.7.04-9.72-.47-14.73-1.33-1.7-.3-3.26-.61-4.67-.93a31.55 31.55 0 0 0-3.55-.57 273.4 273.4 0 0 0-16.66-.88c-10.42-.16-17.2.74-17.97 2.73-.38.97.6 2.55 3.03 4.87 1.01.97 2.22 2.03 4.04 3.55a1746.07 1746.07 0 0 0 4.79 4.02c1.39 1.2 3.1 1.92 5.5 2.5.7.16.86.2 2.64.54 3.53.7 5.03 1.25 6.15 2.63 1.41 1.76 1.4 4.54-.15 8.88-2.44 6.83-5.72 10.05-10.19 10.33-3.63.23-7.6-1.29-14.52-5.06-4.53-2.47-6.82-7.3-8.32-15.26-.17-.87-.32-1.78-.5-2.86l-.43-2.76c-1.05-6.58-1.9-9.2-3.73-10.11-.81-.4-1.59-.74-2.36-1-2.27-.77-4.6-1.02-8.1-.92-2.29.07-14.7 1-13.77.93-20.55 1.37-28.8 5.05-37.09 14.99a133.07 133.07 0 0 0-4.25 5.44l-2.3 3.09-2.51 3.32c-4.1 5.36-7.06 8.48-10.39 11.12-.65.52-1.33 1.04-2.13 1.62l-4.11 2.94a106.8 106.8 0 0 0-5.16 3.99c-4.55 3.74-9.74 8.6-16.25 15.38-8.25 8.58-11.78 13.54-11.7 15.95.07 1.65 1.64 2.11 6.79 2.38 1.61.09 2.15.12 2.98.2 2.95.24 5.09.73 6.81 1.68 7.48 4.15 11.63 7.26 13.95 11.58 3.3 6.15.8 12.88-8.89 20.26-8.28 6.3-11.1 10.37-11.31 14.96-.06 1.17 0 1.93.26 4.43.69 6.47.25 10.65-2.8 17.42a44.23 44.23 0 0 1-4.16 7.53c-2.82 3.97-5.47 5.74-10.6 7.69-.43.16-3.34 1.23-4.27 1.59-1.8.68-3.38 1.36-5.01 2.14-4.18 2-8.4 4.6-13.1 8.24-8.44 6.51-13.23 14.56-15.98 25.06-1.1 4.2-1.55 6.81-2.8 15.21-1.26 8.6-2.17 12.64-4.08 16.55-2.1 4.28-11.93 26.59-12.97 28.88a382.7 382.7 0 0 1-6.37 13.41c-4.07 8.11-7.61 14.07-10.73 17.81-5.38 6.46-8.98 14.37-13.77 28.42a810.14 810.14 0 0 0-1.89 5.6c-1.8 5.35-2.96 8.6-4.26 11.85-6.13 15.32-25.43 26.31-46.46 26.31-11.2 0-20.58-2.74-31.02-8.55-5.6-3.13-4.55-2.42-22.26-14.54-14.33-9.8-17.7-10.73-20.47-6.9-.37.5-1.81 2.74-1.83 2.77a52.24 52.24 0 0 1-4.94 5.9c-.73.79-5.52 5.87-6.97 7.45-2.38 2.6-4.3 4.81-5.98 6.93a45.6 45.6 0 0 0-5.08 7.66c-1.29 2.57-1.9 5.25-2.66 10.6a997.6 997.6 0 0 1-.46 3.18h-1l.47-3.32c.77-5.45 1.4-8.2 2.75-10.9a46.54 46.54 0 0 1 5.2-7.84c1.7-2.14 3.63-4.38 6.03-6.98 1.45-1.59 6.24-6.68 6.96-7.46a51.58 51.58 0 0 0 4.84-5.78s1.47-2.26 1.86-2.8c3.25-4.5 7.08-3.44 21.84 6.67 17.67 12.08 16.62 11.38 22.19 14.48 10.3 5.73 19.5 8.43 30.53 8.43 20.65 0 39.57-10.77 45.54-25.69a219.7 219.7 0 0 0 4.24-11.8 6752.32 6752.32 0 0 0 1.88-5.6c4.83-14.16 8.47-22.14 13.96-28.73 3.05-3.66 6.56-9.57 10.6-17.61 1.97-3.93 4.04-8.31 6.35-13.38 1.03-2.28 10.88-24.61 12.98-28.91 1.85-3.79 2.75-7.76 4-16.25 1.24-8.44 1.7-11.07 2.81-15.32 2.8-10.7 7.71-18.94 16.33-25.6a73.18 73.18 0 0 1 13.29-8.35c1.66-.8 3.27-1.48 5.08-2.18.94-.36 3.86-1.43 4.28-1.59 4.95-1.88 7.44-3.55 10.14-7.33 1.35-1.9 2.68-4.3 4.06-7.37 2.97-6.58 3.39-10.59 2.72-16.9a27.13 27.13 0 0 1-.27-4.58c.22-4.94 3.21-9.24 11.7-15.7 9.33-7.11 11.66-13.34 8.62-19-2.2-4.09-6.25-7.12-13.55-11.17-1.57-.88-3.6-1.33-6.42-1.57-.8-.07-1.34-.1-2.95-.19-5.77-.3-7.63-.85-7.72-3.34-.1-2.81 3.5-7.87 11.97-16.69 6.53-6.8 11.75-11.69 16.33-15.45 1.79-1.47 3.42-2.72 5.2-4.03l4.12-2.94c.79-.58 1.46-1.08 2.1-1.59 3.26-2.6 6.16-5.65 10.21-10.94a383.2 383.2 0 0 0 2.5-3.32l2.31-3.09c1.8-2.39 3.04-4 4.29-5.48 8.47-10.17 16.98-13.96 37.27-15.3-.44.02 12-.9 14.32-.98 3.62-.1 6.05.16 8.46.98.8.27 1.62.62 2.47 1.04 2.27 1.14 3.17 3.87 4.27 10.85l.44 2.76c.17 1.07.33 1.97.5 2.83 1.44 7.69 3.62 12.29 7.8 14.57 6.76 3.68 10.6 5.15 13.99 4.94 4-.25 6.99-3.17 9.3-9.67 1.45-4.04 1.46-6.49.32-7.92-.9-1.12-2.28-1.62-5.57-2.27a55.8 55.8 0 0 1-2.67-.55c-2.54-.6-4.39-1.4-5.93-2.71a252.63 252.63 0 0 0-4.78-4.01 84.35 84.35 0 0 1-4.08-3.6c-2.73-2.6-3.86-4.43-3.28-5.95 1.02-2.64 7.82-3.54 18.93-3.37a230.56 230.56 0 0 1 16.73.88c2.76.39 3.2.49 3.68.6 1.4.3 2.95.62 4.62.91a82.9 82.9 0 0 0 14.56 1.32c5.56-.04 10.24-.86 13.73-2.6 8.1-4.05 15.89-6.9 22.17-7.56.7-.07 1.4-.11 2.05-.13v1zm0-100.94v1.5c-8.62 16.05-17.27 29.55-23.65 35.92-3.19 3.2-7.62 4.9-13.54 5.56-4.45.48-8.28.4-19.18-.2-9.91-.55-15.32-.44-20.52.78a84.05 84.05 0 0 1-15 2.11l-2.25.14c-12.49.75-19.37 1.78-32.72 5.74-4.5 1.33-9.27 2.49-14.3 3.48a246.27 246.27 0 0 1-32.6 3.97c-7.56.45-13.21.57-20.24.57-5.4 0-11.9 1.61-18 5.18-8.3 4.87-15.06 12.87-19.53 24.5a68.57 68.57 0 0 1-4.56 9.8c-3.6 6.2-6.92 8.99-13.38 12.18l-4.03 1.96a64.48 64.48 0 0 0-15.16 10.25c-8.2 7.33-13.72 16.63-22.54 35.6l-2.08 4.49c-7.3 15.7-11.5 23.3-17.35 29.87-7.7 8.66-20.25 14.42-40.31 20.08-4.37 1.23-19.04 5.08-19.24 5.13-6.92 1.87-11.68 3.34-15.63 4.92-10.55 4.22-18.71 10.52-36.38 26.52l-1.7 1.54c-8.58 7.76-13.41 11.9-18.81 15.88-3.95 2.9-8 5.67-12.97 8.91-2.06 1.34-10.3 6.6-12.33 7.94-11.52 7.5-18.53 13.04-24.62 20.08a62.01 62.01 0 0 0-6.44 8.85c-4.13 6.91-6.27 13.15-9.2 25.11l-1.54 6.26c-.6 2.45-1.15 4.54-1.72 6.58-2.97 10.7-6.9 17.36-14.78 26.91L69.6 491a148.51 148.51 0 0 0-4.19 5.3 23.9 23.9 0 0 0-3.44 6.28c-1.16 3.23-1.52 5.9-1.87 11.94-.58 10.05-1.42 15.04-4.63 22.67-1.57 3.72-5.66 14.02-6.41 15.8a73.46 73.46 0 0 1-3.57 7.4c-2.88 5.14-6.71 10.12-13.12 16.95-5.96 6.36-8.87 10.9-10.61 16a56.88 56.88 0 0 0-1.38 4.82l-.46 1.84h-1.03l.52-2.08c.52-2.09.92-3.49 1.4-4.9 1.8-5.25 4.78-9.9 10.84-16.36 6.35-6.78 10.13-11.7 12.97-16.77a72.5 72.5 0 0 0 3.52-7.29c.75-1.76 4.84-12.06 6.4-15.8 3.17-7.5 3.99-12.4 4.56-22.33.35-6.14.72-8.88 1.93-12.23a24.9 24.9 0 0 1 3.58-6.54c1.27-1.7 2.6-3.37 4.22-5.34l4.11-4.95c7.8-9.46 11.66-16 14.59-26.54.56-2.04 1.1-4.12 1.71-6.56l1.53-6.26c2.96-12.04 5.13-18.36 9.32-25.39 1.84-3.08 4-6.05 6.54-8.99 6.17-7.12 13.24-12.7 24.83-20.26 2.05-1.33 10.28-6.6 12.33-7.94 4.96-3.22 9-5.98 12.92-8.87 5.37-3.95 10.19-8.08 18.74-15.82l1.7-1.54c17.76-16.09 25.98-22.43 36.67-26.7 4-1.6 8.8-3.09 15.75-4.96.21-.06 14.87-3.9 19.22-5.13 19.9-5.61 32.32-11.31 39.85-19.78 5.76-6.48 9.93-14.02 17.18-29.64l2.09-4.5c8.87-19.07 14.44-28.46 22.77-35.9a65.48 65.48 0 0 1 15.38-10.4l4.04-1.97c6.3-3.1 9.47-5.77 12.96-11.77a67.6 67.6 0 0 0 4.48-9.67c4.56-11.84 11.47-20.02 19.97-25 6.25-3.66 12.93-5.32 18.5-5.32 7.01 0 12.65-.12 20.17-.57a245.3 245.3 0 0 0 32.47-3.96c5-.98 9.75-2.13 14.22-3.45 13.43-3.98 20.38-5.02 32.94-5.78l2.24-.14c5.76-.37 9.8-.9 14.85-2.09 5.31-1.25 10.79-1.35 22.6-.7 9.04.5 12.84.58 17.21.1 5.71-.62 9.94-2.26 12.95-5.26 6.44-6.45 15.3-20.37 24.35-36.72zm0 450.21c-1.28-4.6-2.2-10.55-3.33-20.25l-.24-2.04-.23-2.03c-1.82-15.7-3.07-21.98-5.55-24.47-2.46-2.46-3.04-5.03-2.52-8.64.1-.6.18-1.1.39-2.15.69-3.54.77-5.04.08-6.84-.91-2.38-3.31-4.41-7.79-6.26-5.08-2.09-6.52-4.84-4.89-8.44.66-1.45 1.79-3.02 3.52-5.01 1.04-1.2 5.48-5.96 5.08-5.53 6.15-6.7 8.98-11.34 8.98-16.48a15.2 15.2 0 0 1 6.5-12.89v1.26a14.17 14.17 0 0 0-5.5 11.63c0 5.47-2.93 10.29-9.24 17.16.38-.42-4.04 4.33-5.07 5.5-1.67 1.93-2.75 3.43-3.36 4.77-1.37 3.04-.23 5.22 4.36 7.1 4.71 1.95 7.32 4.16 8.34 6.83.78 2.04.7 3.67-.03 7.4-.2 1.03-.3 1.51-.38 2.09-.48 3.33.03 5.59 2.23 7.8 2.74 2.74 3.98 8.96 5.84 25.06l.24 2.03.23 2.04c.82 7.01 1.53 12.06 2.34 16.03v4.33zm0-62.16c-1.4-3.13-4.43-9.9-4.95-11.17-1.02-2.53-1.25-3.8-.91-5.18.2-.84 2.05-4.68 2.32-5.33a70.79 70.79 0 0 0 3.54-11.2v3.99a62.82 62.82 0 0 1-2.62 7.6c-.31.75-2.09 4.46-2.27 5.18-.28 1.12-.08 2.22.87 4.57.41 1.02 2.5 5.7 4.02 9.09v2.45zm0-85.09c-1.65 1.66-3.66 2.9-6.4 4.13-.25.1-13.97 5.47-20.4 8.43-9.35 4.32-16.7 5.9-23.03 5.25-5.08-.53-9.02-2.25-14.77-5.92l-3.2-2.07a77.4 77.4 0 0 0-5.44-3.27c-4.05-2.18-3.25-5.8 1.47-10.47 3.71-3.68 9.6-7.93 18.73-13.8l4.46-2.82c17.95-11.33 18.22-11.5 22.27-14.74 11.25-9 19.69-14.02 26.31-15.1v1.02c-6.37 1.1-14.62 6-25.69 14.86-4.1 3.28-4.34 3.44-22.36 14.8a652.4 652.4 0 0 0-4.45 2.83c-9.07 5.83-14.92 10.05-18.57 13.66-4.31 4.28-4.95 7.13-1.7 8.88 1.7.91 3.29 1.88 5.5 3.3l3.2 2.08c5.64 3.59 9.45 5.25 14.34 5.76 6.13.64 13.32-.9 22.52-5.15 6.46-2.98 20.18-8.35 20.4-8.44 3.04-1.37 5.1-2.71 6.81-4.69v1.47zm0-41.37v1c-6.56.26-12.11 3.13-19.71 9.08l-4.63 3.68a51.87 51.87 0 0 1-4.4 3.14c-.82.52-5.51 3.33-6.22 3.76-3.31 2-6.15 3.8-8.87 5.6a112.61 112.61 0 0 0-8.16 5.92c-4.61 3.72-7.4 6.9-7.97 9.35-.63 2.67 1.48 4.53 7.05 5.46 10.7 1.78 20.92-.05 30.45-4.65a61.96 61.96 0 0 0 17.1-12.2 41.8 41.8 0 0 0 5.36-7.42v1.92a38.94 38.94 0 0 1-4.64 6.19 62.95 62.95 0 0 1-17.39 12.41c-9.7 4.68-20.13 6.55-31.05 4.73-6.06-1-8.65-3.29-7.85-6.67.64-2.74 3.53-6.05 8.31-9.9 2.35-1.9 5.1-3.88 8.24-5.97 2.73-1.82 5.58-3.61 8.9-5.62.72-.44 5.4-3.24 6.22-3.75 1.26-.8 2.6-1.76 4.3-3.09.8-.62 3.9-3.1 4.63-3.67 7.77-6.1 13.49-9.04 20.33-9.3zm0-154.6v1c-1.75-.24-4.3.23-7.82 1.55-10.01 3.75-13.8 5.07-19.15 6.76-1.78.56-2.63.83-3.87 1.24-1.48.5-3.16.76-6.74 1.16a1550.34 1550.34 0 0 0-2.64.3c-7.8.94-11.28 2.47-11.28 6.07 0 4.45 2.89 13.18 7.96 25.81a57.34 57.34 0 0 1 2.33 7.6 258.32 258.32 0 0 1 .84 3.46c1.86 7.62 3.17 10.71 5.56 11.67 2.21.88 4.7.6 7.47-.72 3.48-1.69 7.22-4.94 11.2-9.47 1.52-1.7 2.97-3.49 4.59-5.57l3.16-4.1c2.59-3.23 6.07-12.21 8.39-20.23v3.45c-2.29 7.2-5.27 14.5-7.61 17.41-.44.55-2.67 3.46-3.15 4.09-1.63 2.1-3.1 3.9-4.62 5.62-4.08 4.61-7.9 7.94-11.53 9.7-2.99 1.44-5.77 1.75-8.28.74-2.84-1.13-4.2-4.34-6.15-12.35a2097.48 2097.48 0 0 1-.84-3.46c-.8-3.2-1.47-5.45-2.28-7.46-5.14-12.8-8.04-21.55-8.04-26.19 0-4.37 3.84-6.06 12.16-7.07a160.9 160.9 0 0 1 2.65-.3c3.5-.39 5.15-.64 6.53-1.1 1.26-.42 2.1-.7 3.88-1.26 5.34-1.68 9.11-3 19.1-6.74 3.53-1.32 6.22-1.84 8.18-1.61zM0 292c10.13-11.31 18.13-23.2 23.07-35.39 3.3-8.14 6.09-16.12 10.81-30.55l1.59-4.84c6.53-19.94 10.11-29.82 14.77-39.56 6.07-12.72 12.55-21.18 20.27-25.54 6.66-3.76 10.2-7.86 12.22-13.15a46.6 46.6 0 0 0 1.86-6.58c1.23-5.2 2.05-7.59 3.93-10.36 2.45-3.62 6.27-6.53 12.1-8.96 15.78-6.58 16.73-7.04 18.05-9.01.65-.98.83-2.15.74-4.51-.03-.73-.23-3.82-.24-4A93.8 93.8 0 0 1 119 94c0-10.04.18-11.37 2.37-13.15.52-.42 1.13-.8 2.07-1.3.27-.14 2.18-1.12 2.84-1.48a68.4 68.4 0 0 0 9.12-5.87c2.06-1.54 2.64-2.14 8.01-7.93 3.78-4.09 6.21-6.36 8.96-8.12 3.64-2.33 7.2-3.12 10.9-2.11 4.4 1.2 10.81 2 18.78 2.46 6.9.4 12.9.5 21.95.5 4.87 0 8.97.47 15.4 1.57 7.77 1.33 9.3 1.54 12.38 1.54 4.05 0 7.43-.88 10.68-2.95 5.06-3.22 8.11-4.67 11.2-5.2 3.62-.64 4.77-.46 16.55 2.06 17.26 3.7 30.85 1.36 41.06-9.7 5.1-5.53 5.48-8.9 3.48-14.8-.83-2.42-1.03-3.1-1.17-4.3-.29-2.52.5-4.71 2.71-6.93 2.65-2.65 4.72-9.17 6.22-18.29h2.03c-1.56 9.71-3.77 16.65-6.83 19.7-1.79 1.8-2.36 3.39-2.14 5.28.11 1 .3 1.63 1.07 3.9 2.22 6.53 1.76 10.66-3.9 16.8-10.77 11.66-25.07 14.13-42.95 10.3-11.42-2.45-12.55-2.62-15.78-2.06-2.77.48-5.62 1.84-10.47 4.92a20.93 20.93 0 0 1-11.76 3.27c-3.25 0-4.81-.22-12.73-1.57C212.74 59.46 208.73 59 204 59c-9.1 0-15.11-.1-22.07-.5-8.09-.47-14.62-1.29-19.2-2.54-5.62-1.53-10.17 1.38-17.85 9.66-5.5 5.94-6.08 6.53-8.28 8.18a70.38 70.38 0 0 1-9.38 6.03c-.68.37-2.58 1.35-2.84 1.49-.84.44-1.35.76-1.75 1.08C121.16 83.6 121 84.8 121 94c0 1.85.06 3.54.17 5.44 0 .17.2 3.28.24 4.03.1 2.75-.13 4.29-1.08 5.71-1.67 2.5-2.27 2.8-18.95 9.74-5.48 2.29-8.99 4.96-11.2 8.24-1.71 2.51-2.47 4.73-3.64 9.7-.83 3.5-1.21 4.92-1.94 6.83-2.18 5.73-6.05 10.19-13.1 14.18-7.3 4.12-13.55 12.28-19.46 24.66-4.6 9.64-8.17 19.46-14.67 39.32l-1.58 4.84c-4.75 14.47-7.54 22.48-10.86 30.69-5.28 13.01-13.95 25.65-24.93 37.6v-2.97zm0 78v-.5l1-.01c6.32 0 7.47 5.2 4.6 13.36a60.36 60.36 0 0 1-5.6 11.3v-1.92a57.76 57.76 0 0 0 4.65-9.72c2.69-7.6 1.71-12.02-3.65-12.02-.34 0-.67 0-1 .02v-46.59a340.96 340.96 0 0 0 13.71-8.34c13.66-9.46 29.79-37.6 29.79-53.59 0-18.1 21.57-72.64 32.23-79.42 12.71-8.09 32.24-27.96 35.8-37.75 1.93-5.3 5.5-7.27 14.42-9.37 6.15-1.44 8.64-2.42 10.67-4.79 1.5-1.74 2.72-4.79 4.33-10.3.23-.78 1.9-6.68 2.43-8.46 3.62-12.08 7.3-18.49 13.47-20.39 2.5-.76 3.03-.98 9.74-3.7 7.49-3.03 11.97-4.43 17.12-4.92 6.75-.65 13.13.75 19.55 4.67 5.43 3.32 12.19 4.72 20.17 4.56 6.03-.12 12.2-1.07 19.83-2.8 1.82-.4 7.38-1.74 8.26-1.94 2.69-.6 4.34-.89 5.48-.89 4.97 0 8.93-.05 14.2-.27 7.9-.32 15.56-.92 22.75-1.88 8.5-1.14 15.9-2.73 21.88-4.82 18.9-6.62 32.64-18.3 33.67-27.59.29-2.56.4-2.96 2.79-11.11 2.33-7.95 3.21-12.93 2.72-18.23-.2-2.24-.69-4.38-1.48-6.42-1.5-3.92-2.63-9.4-3.43-16.18h.9c.77 6.47 1.89 11.72 3.47 15.82a24.93 24.93 0 0 1 1.54 6.69c.5 5.46-.4 10.54-2.77 18.6-2.36 8.06-2.47 8.47-2.74 10.95-1.09 9.75-15.1 21.68-34.33 28.41-6.06 2.12-13.52 3.72-22.09 4.87-7.22.96-14.92 1.57-22.83 1.89-5.3.21-9.27.27-14.25.27-1.04 0-2.64.27-5.26.87-.87.2-6.43 1.53-8.26 1.94-7.68 1.73-13.92 2.7-20.03 2.82-8.15.17-15.1-1.27-20.71-4.7-6.23-3.81-12.4-5.16-18.93-4.54-5.04.48-9.44 1.86-16.84 4.86-6.75 2.74-7.29 2.95-9.82 3.73-5.73 1.76-9.28 7.96-12.81 19.72-.53 1.77-2.2 7.66-2.43 8.46-1.66 5.65-2.91 8.78-4.53 10.67-2.22 2.58-4.84 3.62-12.01 5.3-7.8 1.83-11.13 3.66-12.9 8.54-3.65 10.04-23.32 30.06-36.2 38.25C65.94 190 44.5 244.2 44.5 262c0 16.34-16.3 44.78-30.22 54.41-2.14 1.48-8.24 5.12-14.28 8.68v-1.16 46.09zm0-173.7v-1.11c7.42-3.82 14.55-10.23 21.84-18.98 3.8-4.56 14.21-18.78 15.79-20.55 1.8-2.04 4.06-3.96 7.42-6.45 1.08-.8 4.92-3.57 5.49-3.99 9.36-6.85 14-11.96 15.98-19.36.8-2.98 1.54-6.78 2.46-12.3.23-1.44 2-12.46 2.56-15.79 2.87-16.77 5.73-26.79 10.07-32.1C92.46 52.43 101.5 38.13 101.5 33c0-2.54.34-3.35 6.05-15.71.68-1.49 1.25-2.74 1.77-3.93 2.5-5.75 3.9-10.04 4.14-13.36h1c-.23 3.48-1.66 7.87-4.23 13.76-.52 1.2-1.09 2.45-1.78 3.95-5.54 12.01-5.95 12.99-5.95 15.29 0 5.47-9.09 19.84-20.11 33.31-4.2 5.12-7.03 15.06-9.86 31.64-.57 3.33-2.33 14.33-2.57 15.78-.92 5.56-1.67 9.38-2.48 12.4-2.05 7.68-6.82 12.93-16.35 19.91l-5.49 3.98c-3.3 2.45-5.51 4.34-7.27 6.31-1.53 1.73-11.94 15.93-15.76 20.53-7.52 9.02-14.88 15.6-22.61 19.46zm0 361.83v-4.33c.48 2.36 1 4.35 1.6 6.15 2 6.03 4.6 8.26 8.19 6.59C28.76 557.69 43.5 542.4 43.5 527c0-16.2 6.37-31.99 17.1-46.3 1.88-2.5 3.66-4.4 5.53-6 .73-.62 1.45-1.18 2.3-1.8l2-1.43c3.68-2.68 5.32-5.28 7.08-12.59.75-3.07 1.38-5.02 4.2-13.26l.63-1.88c3.24-9.58 4.56-14.97 4.17-18.65-.48-4.43-3.8-5.23-11.3-1.64a81.12 81.12 0 0 1-9.15 3.7c-13.89 4.67-26.96 5.8-42.66 5.42l-1.95-.05-1.45-.02a39.8 39.8 0 0 0-15.05 2.96A21.81 21.81 0 0 0 0 438.37v-1.26a23.55 23.55 0 0 1 4.55-2.57 40.77 40.77 0 0 1 16.92-3.02l1.95.05c15.6.38 28.57-.75 42.32-5.37a80.12 80.12 0 0 0 9.04-3.65c8.04-3.84 12.16-2.85 12.72 2.43.42 3.89-.92 9.34-4.21 19.08l-.64 1.88c-2.8 8.2-3.43 10.15-4.16 13.18-1.82 7.52-3.59 10.34-7.47 13.16l-2 1.43c-.84.6-1.54 1.15-2.25 1.75a35.45 35.45 0 0 0-5.37 5.84c-10.61 14.15-16.9 29.74-16.9 45.7 0 15.88-15 31.45-34.29 40.45-4.3 2.01-7.39-.66-9.56-7.18-.23-.68-.44-1.39-.65-2.13zm0-62.16v-2.45l1.46 3.27c2.1 4.8 3.46 10.33 4.26 16.77.66 5.3.84 9.3 1.04 18.5.2 9.32.5 12.75 1.63 15.05 1.28 2.6 3.67 2.35 8.29-1.5 17.14-14.3 21.82-22.9 21.82-38.62 0-7.17 1.1-12.39 3.7-17.68 2.27-4.67 3.65-6.62 13.4-19.62a69.8 69.8 0 0 1 7.6-8.79 44.76 44.76 0 0 1 3.54-3.06c.38-.3.64-.52.89-.74a10.47 10.47 0 0 0 2.63-3.32 35.78 35.78 0 0 0 2.26-5.94l.37-1.2.36-1.15c.29-.91.48-1.55.66-2.16.45-1.53.74-2.68.91-3.66.38-2.2.12-3.49-.85-4.15-2.35-1.61-9.28-.24-23.8 4.94-9.54 3.4-16.12 4.17-27.85 4.26-7.71.06-10.43.4-13.25 2.12-3.48 2.12-5.84 6.4-7.58 14.26-.5 2.2-.99 4.19-1.49 5.98v-3.98l.51-2.22c1.8-8.1 4.28-12.6 8.04-14.9 3.04-1.85 5.86-2.2 13.77-2.26 11.61-.09 18.1-.84 27.51-4.2 14.93-5.32 21.95-6.71 24.7-4.83 1.38.94 1.71 2.6 1.28 5.15a33.69 33.69 0 0 1-.94 3.78l-.66 2.17-.36 1.15-.37 1.2a36.64 36.64 0 0 1-2.33 6.1c-.8 1.53-1.61 2.52-2.86 3.61l-.92.77-1.02.83c-.9.74-1.65 1.4-2.47 2.18a68.84 68.84 0 0 0-7.48 8.66c-9.7 12.93-11.07 14.87-13.31 19.46-2.52 5.15-3.59 10.22-3.59 17.24 0 16.04-4.82 24.91-22.18 39.38-5.04 4.2-8.18 4.55-9.83 1.18-1.22-2.5-1.52-5.94-1.73-15.47-.2-9.16-.38-13.15-1.03-18.4-.79-6.34-2.12-11.8-4.19-16.49L0 495.98zM379.27 0h1.04l1.5 5.26c3.28 11.56 4.89 19.33 5.26 27.8.49 11.01-1.52 21.26-6.63 31.17-7.8 15.13-20.47 26.5-36.22 34.1-12.38 5.96-26.12 9.17-36.22 9.17-6.84 0-17.24 1.38-37.27 4.62l-2.27.37c-24.5 3.99-31.65 5-37.46 5-3.49 0-4.08-.08-19.54-2.8-3.56-.64-6.32-1.1-9-1.5-20.23-2.96-31-1.2-31.96 7.86-.1.85-.18 1.72-.29 2.81l-.27 2.73c-1.1 10.9-2.02 15.73-4.31 19.96-2.9 5.34-7.77 7.95-15.63 7.95-10.2 0-12.92.6-15.5 3.17.52-.51-5.03 5.85-8.16 8.7-2.75 2.5-14.32 12.55-15.77 13.83a341.27 341.27 0 0 0-6.54 5.92c-6.97 6.49-11.81 11.76-14.6 16.15-5.92 9.3-10.48 18.04-11.69 24.08-1.66 8.3 3.67 9.54 19.02 1.21a626.23 626.23 0 0 1 44.54-21.9c3.5-1.56 14.04-6.2 15.68-6.95 5.05-2.25 8.3-3.8 10.78-5.15l1.95-1.07 2.18-1.18c1.76-.94 3.38-1.76 5-2.55 18.1-8.72 34.48-10.46 50.33-1.2 22.89 13.34 38.28 37.02 38.28 56.44 0 19.12-.73 25.13-5.18 33.2a45.32 45.32 0 0 1-4.94 7.12c-6.47 7.77-11.81 16.2-12.76 21.27-1.2 6.34 4.69 7.03 20.17-.05 13.31-6.08 22.4-14.95 28.5-26.32a80.51 80.51 0 0 0 6.1-15.13c.9-2.98 3.17-11.65 3.41-12.48a29.02 29.02 0 0 1 1.75-4.83c7.47-14.93 21.09-30.5 36.25-37.24 7.61-3.38 13-9.65 19.4-20.79.84-1.48 4.26-7.64 5.14-9.17 3.52-6.1 6.22-9.7 9.37-11.98 10.15-7.4 28.7-11.1 50.29-11.1 7.52 0 16.54-1.24 27.51-3.58a420.1 420.1 0 0 0 14.96-3.52c-1.3.33 15.54-3.98 19.42-4.89 14.15-3.33 41.07-5.01 64.11-5.01 17.36 0 27.82-9.23 38.53-38.67 6.62-18.21 6.62-26.37 2.69-34.35l-1.18-2.37A13.36 13.36 0 0 1 587.5 58c0-4.03 0-4.01 2.5-24.56.46-3.73.8-6.74 1.12-9.64.9-8.45 1.38-15.2 1.38-20.8 0-.94-.02-1.94-.04-3h1c.03 1.06.04 2.06.04 3 0 5.65-.48 12.43-1.39 20.9-.3 2.91-.66 5.93-1.11 9.66-2.5 20.45-2.5 20.47-2.5 24.44 0 1.97.45 3.57 1.45 5.68.24.51 1.16 2.35 1.17 2.36 4.06 8.24 4.06 16.68-2.65 35.13-10.84 29.8-21.63 39.33-39.47 39.33-22.96 0-49.83 1.68-63.89 4.99-3.86.9-20.69 5.2-19.4 4.88a421.05 421.05 0 0 1-14.99 3.53c-11.04 2.35-20.11 3.6-27.72 3.6-21.4 0-39.76 3.67-49.7 10.9-3 2.19-5.64 5.7-9.1 11.68-.87 1.52-4.29 7.68-5.14 9.17-6.49 11.3-12 17.71-19.86 21.2-14.9 6.63-28.38 22.03-35.75 36.77a28.17 28.17 0 0 0-1.69 4.67c-.23.8-2.5 9.49-3.4 12.5a81.48 81.48 0 0 1-6.19 15.3c-6.2 11.56-15.44 20.58-28.96 26.76-16.1 7.36-23 6.55-21.58-1.04 1-5.29 6.4-13.83 12.99-21.73a44.33 44.33 0 0 0 4.82-6.96c4.35-7.88 5.06-13.77 5.06-32.72 0-19.04-15.19-42.4-37.72-55.55-15.57-9.08-31.62-7.38-49.45 1.21a132.9 132.9 0 0 0-7.14 3.71l-1.95 1.07a158.83 158.83 0 0 1-10.85 5.19c-1.65.74-12.18 5.38-15.69 6.95a625.25 625.25 0 0 0-44.46 21.86c-15.95 8.66-22.37 7.16-20.48-2.29 1.24-6.2 5.83-15.02 11.82-24.42 2.85-4.48 7.74-9.8 14.77-16.34 1.98-1.85 4.12-3.79 6.56-5.94 1.46-1.29 13.02-11.33 15.75-13.82 3.09-2.8 8.6-9.14 8.14-8.67 2.82-2.82 5.75-3.46 16.2-3.46 7.5 0 12.04-2.43 14.75-7.42 2.2-4.07 3.11-8.84 4.2-19.59l.26-2.73.3-2.81c.56-5.42 4.47-8.5 11.23-9.6 5.44-.88 12.51-.51 21.86.86 2.7.4 5.47.86 9.04 1.49 15.33 2.7 15.96 2.8 19.36 2.8 5.73 0 12.9-1.03 37.3-5l2.27-.36c20.1-3.26 30.52-4.64 37.43-4.64 9.95 0 23.54-3.18 35.78-9.08 15.57-7.5 28.09-18.73 35.78-33.65 5.02-9.75 7-19.82 6.51-30.67-.37-8.37-1.96-16.08-5.23-27.57L379.27 0zm13.68 0h1.02c.78 3.9 1.92 8.7 3.51 14.88 3.63 14.05 3.06 27.03-.75 38.77a61 61 0 0 1-11.35 20.68 138.36 138.36 0 0 1-19.32 18.77c-11.32 9.02-23.36 15.49-35.95 18.39a258.63 258.63 0 0 1-22.57 4.07c-3.17.44-6.36.85-10.3 1.32l-9.39 1.12c-11.53 1.41-17.45 2.55-21.64 4.46-9.28 4.21-28.35 6.04-49.21 6.04-1.37 0-2.8-.12-4.3-.35-2.62-.41-5-1.03-9.14-2.29-7.34-2.21-9.63-2.75-12.63-2.56-3.9.23-6.63 2.29-8.47 6.89-1.86 4.66-2.42 7.53-3.34 14.98-1.1 8.98-2.87 12.12-9.97 14.3a40.12 40.12 0 0 0-6.8 2.66c-.63.33-1.16.64-1.76 1.02l-1.34.86c-1.9 1.14-3.86 1.49-9.25 1.49-3.2 0-8.83-.55-9.51-.39-1.22.28-.75-.14-7.14 6.24-1.5 1.5-3.49 3.18-6.32 5.37-1.52 1.18-7.16 5.43-7.94 6.03-4.96 3.78-8.33 6.6-11.06 9.38-4.88 4.98-6.85 9.15-5.56 12.7 1.34 3.67 4.07 4.42 8.9 2.82a55.72 55.72 0 0 0 7.77-3.48c1.5-.77 7.78-4.13 9.37-4.96a116.8 116.8 0 0 1 12.31-5.68 162.2 162.2 0 0 0 11.04-4.84c2.04-.97 10.74-5.16 13-6.22 4.41-2.1 8.1-3.78 11.65-5.29 17.14-7.3 29.32-9.9 37.67-6.65l5.43 2.1c2.3.88 4.17 1.62 6.02 2.38a150.9 150.9 0 0 1 13.07 6c18.34 9.63 30.35 22.13 34.79 39.87 6.96 27.85 3.6 45.53-8.08 62.4-3.97 5.75-3.52 9.2.06 8.97 4.14-.28 10.21-4.95 15.11-12.52 3.1-4.8 5.1-10.45 8.05-21.53l1.69-6.35c.66-2.47 1.24-4.52 1.83-6.5 4.93-16.56 11-27.28 21.56-34.76 7.15-5.06 23.73-15.5 25.48-16.75 6.74-4.81 10.53-9.44 14.34-18 7.74-17.44 21.09-24.34 44.47-24.34 9.36 0 17.91-1.13 29.53-3.49a624.86 624.86 0 0 0 6.2-1.28c2.4-.5 4.07-.84 5.66-1.13 4.03-.74 7.04-1.1 9.61-1.1 4.44 0 9.39-1 31.39-5.99l2.95-.66c16.34-3.67 25.64-5.35 31.66-5.35 1.54 0 2.4.01 6.4.1 7.8.15 12.27.13 17.33-.2 16.41-1.06 26.73-5.36 29.8-14.56a87.1 87.1 0 0 1 3.55-8.83c-.15.31 2.29-4.96 2.9-6.38 5.38-12.3 5.57-21.92-1.44-39.44a86.4 86.4 0 0 1-5.26-20.72c-1.61-11.98-1.38-23.14.1-40.35l.2-2.12h1l-.2 2.2c-1.48 17.15-1.7 28.24-.11 40.14a85.4 85.4 0 0 0 5.2 20.47c7.1 17.78 6.91 27.67 1.43 40.22-.62 1.43-3.06 6.72-2.91 6.4a86.17 86.17 0 0 0-3.52 8.73c-3.23 9.72-13.9 14.15-30.68 15.24-5.1.33-9.58.35-17.42.2-3.98-.09-4.84-.1-6.37-.1-5.91 0-15.18 1.67-31.44 5.32l-2.95.67c-22.16 5.02-27.05 6.01-31.61 6.01-2.5 0-5.45.36-9.43 1.09-1.58.29-3.25.62-5.64 1.11a4894.21 4894.21 0 0 0-6.2 1.29c-11.68 2.37-20.3 3.51-29.73 3.51-23.02 0-36 6.71-43.53 23.66-3.9 8.8-7.82 13.58-14.7 18.5-1.78 1.27-18.36 11.7-25.48 16.75-10.34 7.32-16.3 17.87-21.19 34.23-.58 1.96-1.15 4-1.82 6.47l-1.69 6.35c-2.98 11.18-5 16.9-8.17 21.81-5.05 7.81-11.37 12.68-15.89 12.98-4.7.31-5.3-4.23-.94-10.53 11.52-16.64 14.82-34.03 7.92-61.6-4.35-17.42-16.16-29.72-34.27-39.22-4-2.1-8.2-4-12.99-5.97-1.84-.75-3.7-1.49-6-2.38l-5.43-2.08c-8.03-3.12-20.02-.58-36.92 6.63-3.52 1.5-7.21 3.19-11.61 5.27l-13 6.22c-4.71 2.22-8.16 3.75-11.11 4.88a115.87 115.87 0 0 0-12.21 5.63c-1.58.83-7.86 4.18-9.37 4.96a56.55 56.55 0 0 1-7.9 3.54c-5.3 1.75-8.62.85-10.17-3.43-1.46-4.02.66-8.5 5.8-13.74 2.75-2.82 6.16-5.66 11.15-9.48.79-.6 6.43-4.85 7.94-6.02a66.96 66.96 0 0 0 6.23-5.28c6.74-6.74 6.1-6.16 7.61-6.51.87-.2 6.69.36 9.74.36 5.22 0 7.03-.32 8.74-1.35l1.31-.84c.62-.4 1.18-.72 1.84-1.07a41.07 41.07 0 0 1 6.96-2.72c6.64-2.04 8.22-4.84 9.28-13.47.93-7.53 1.5-10.47 3.4-15.24 1.99-4.95 5.04-7.26 9.34-7.51 3.17-.2 5.5.35 12.97 2.6a63.54 63.54 0 0 0 9.02 2.26c1.45.22 2.83.34 4.14.34 20.71 0 39.7-1.82 48.8-5.96 4.32-1.96 10.29-3.1 21.93-4.53l9.4-1.12c3.92-.48 7.11-.88 10.27-1.32 8.16-1.14 15.4-2.43 22.49-4.06 12.42-2.86 24.33-9.26 35.55-18.2a137.4 137.4 0 0 0 19.18-18.64 60.02 60.02 0 0 0 11.15-20.32c3.76-11.57 4.32-24.36.75-38.23A284.86 284.86 0 0 1 392.95 0zM506.7 0h1.26c-.5.66-.9 1.18-1.17 1.51-3.95 4.96-6.9 7.92-9.82 9.57A10.02 10.02 0 0 1 492 12.5c-2.38 0-4.24.67-6.71 2.21l-2.65 1.71c-4.38 2.8-8.01 4.08-13.64 4.08-5.6 0-9.99-1.26-16.08-4.05a202.63 202.63 0 0 1-2.3-1.06l-2.18-.98c-1.6-.7-2.92-1.17-4.17-1.48a13.42 13.42 0 0 0-3.27-.43c-2.3 0-4.3-.68-11-3.37l-1.56-.62c-5-1.97-8.1-2.82-10.52-2.66-2.93.2-4.42 2.03-4.42 6.15 0 20.76-5.21 50.42-12.15 57.35-7.58 7.59-26.55 23.7-34.06 29.06-13.16 9.4-31.17 20.2-44.11 25.06a106.87 106.87 0 0 1-13.32 4.03c-3.28.78-6.6 1.43-11.25 2.24-.53.1-8.8 1.5-11.5 1.99-4.86.87-9.3 1.74-14 2.76-20.62 4.48-25.07 5.01-38.11 5.01-2.49 0-2.9-.07-14.05-2-2.42-.42-4.31-.73-6.15-1-8.11-1.19-13.83-1.36-17.64-.2-4.54 1.4-5.93 4.65-3.7 10.52 2.02 5.28 4.84 8.61 8.84 10.74 3.26 1.74 6.75 2.6 13.82 3.71 9.42 1.48 10.94 1.75 15.5 2.92a78.2 78.2 0 0 1 18.62 7.37c8.3 4.58 14.58 11.5 19.98 20.89 2.73 4.73 9.46 19.33 10.54 21.19 3.4 5.85 6.26 6.63 10.89 2 4.95-4.94 10.35-8.37 21.13-14.06.47-.25 2.06-1.1 2.12-1.12 7.98-4.21 11.92-6.51 15.87-9.54 5.11-3.9 8.66-8.1 10.77-13.11 8.52-20.24 20.75-33.31 32.46-33.31l5.5.03c10.53.08 17.35.02 24.9-.31 13.66-.62 23.78-2.09 29.39-4.67 5.85-2.7 13.42-5.49 24.18-9.02 3.46-1.14 6.29-2.05 12.7-4.1 7.7-2.45 11.08-3.54 15.17-4.9a1059.43 1059.43 0 0 1 11.33-3.72c3.67-1.2 5.96-2 8.03-2.78a59.88 59.88 0 0 0 6.66-2.94c1.87-.98 3.76-2.1 5.86-3.5 3.48-2.33 6.15-3.13 12.04-4.13l1.15-.2c5.71-1.01 9-2.3 12.76-5.63 7.82-6.96 8.58-23.18 3.84-44.52-1.7-7.67-2.1-19.28-1.57-35.47A837.22 837.22 0 0 1 546.76 0h1l-.15 3.06c-.32 6.42-.53 11.02-.68 15.62-.51 16.1-.12 27.65 1.56 35.21 4.82 21.68 4.04 38.2-4.16 45.48-3.91 3.48-7.37 4.84-13.24 5.87l-1.16.2c-5.76.99-8.32 1.75-11.65 3.98a63.73 63.73 0 0 1-5.96 3.56 60.86 60.86 0 0 1-6.77 2.99c-2.09.79-4.39 1.58-8.07 2.79a5398.31 5398.31 0 0 1-11.32 3.71c-4.1 1.37-7.48 2.46-15.18 4.92-6.42 2.04-9.24 2.95-12.7 4.08-10.73 3.53-18.27 6.3-24.07 8.98-5.76 2.66-15.97 4.14-29.77 4.77-7.56.33-14.4.39-24.95.31l-5.49-.03c-11.19 0-23.16 12.79-31.54 32.7-2.19 5.19-5.84 9.52-11.08 13.52-4.02 3.07-7.99 5.39-16.01 9.62l-2.12 1.12c-10.7 5.65-16.04 9.04-20.9 13.9-5.14 5.14-8.75 4.15-12.45-2.22-1.12-1.92-7.85-16.5-10.54-21.2-5.33-9.24-11.48-16.02-19.6-20.5a77.2 77.2 0 0 0-18.4-7.28c-4.5-1.17-6.02-1.43-15.4-2.9-7.17-1.12-10.74-2-14.13-3.81-4.22-2.25-7.2-5.77-9.3-11.27-2.43-6.39-.78-10.26 4.34-11.83 4-1.22 9.82-1.05 18.08.17 1.84.27 3.74.58 6.17 1 11.02 1.9 11.48 1.98 13.88 1.98 12.96 0 17.35-.52 37.9-4.99 4.71-1.02 9.16-1.9 14.03-2.77 2.71-.48 10.98-1.9 11.5-1.98 4.64-.81 7.95-1.46 11.2-2.23 4.55-1.07 8.76-2.34 13.2-4 12.83-4.81 30.79-15.59 43.88-24.94 7.47-5.33 26.4-21.4 33.94-28.94C407.3 61.98 412.5 32.49 412.5 12c0-4.61 1.86-6.9 5.35-7.15 2.63-.18 5.8.7 10.96 2.73l1.56.62c6.53 2.62 8.53 3.3 10.63 3.3 1.14 0 2.3.16 3.5.46 1.32.33 2.68.82 4.34 1.53a90.97 90.97 0 0 1 3.34 1.52l1.15.54c5.98 2.73 10.23 3.95 15.67 3.95 5.41 0 8.87-1.21 13.1-3.92.2-.13 2.1-1.38 2.66-1.72 2.62-1.63 4.64-2.36 7.24-2.36 1.47 0 2.94-.43 4.47-1.3 2.78-1.56 5.67-4.45 9.54-9.31l.7-.89zM324.54 600h-2.03c.49-2.96.91-6.2 1.28-9.66.44-4.1.76-8.25.98-12.21.08-1.39.14-2.65-.35-7.29-.47-1.94-.93-4.14-1.36-6.54-2.01-11.26-2.66-22.9-1.14-33.78a60.76 60.76 0 0 1 5.18-17.95 70.78 70.78 0 0 1 12.6-18.22c3.38-3.6 5.53-5.5 11.83-10.79 4.5-3.78 6.35-5.56 7.52-7.5.64-1.07.95-2.06.95-3.06 0-1.75 0-1.74-.75-9.23-.36-3.7-.57-6.3-.68-8.96-.5-12.1 1.62-19.6 8.11-21.76 15.9-5.3 25.89-12.1 33.45-25.54C409.6 390.65 425.85 376 436 376c12.36 0 20-1.96 29.41-8.8 6.76-4.92 9.5-6.6 12.47-7.46 2.22-.64 3.8-.74 9.12-.74 1.86 0 3.53-.83 5.57-2.62 1.08-.96 5.11-5.12 5.6-5.6 6.04-5.85 11.98-8.78 20.83-8.78 2.45 0 4.54.04 7.32.12 7.51.23 8.87.17 11.27-.7 3.03-1.1 5.53-3.03 14.75-11.17 8-7.06 10.72-8.92 22.87-16.47 1.44-.9 2.59-1.63 3.69-2.37a69.45 69.45 0 0 0 9.46-7.5c4.12-3.88 8.02-7.85 11.64-11.9v2.98a201.58 201.58 0 0 1-10.27 10.38c-3.18 3-6.2 5.35-9.72 7.7-1.12.76-2.28 1.5-3.75 2.4-12.05 7.5-14.71 9.32-22.6 16.28-9.46 8.35-12.01 10.32-15.39 11.55-2.74 1-4.19 1.06-12.01.82-2.76-.08-4.83-.12-7.26-.12-8.27 0-13.75 2.7-19.43 8.22-.44.43-4.52 4.64-5.68 5.66-2.37 2.09-4.46 3.12-6.89 3.12-5.1 0-6.6.1-8.56.66-2.67.78-5.29 2.37-11.85 7.15-9.8 7.13-17.85 9.19-30.59 9.19-9.22 0-24.96 14.2-34.13 30.49-7.84 13.94-18.24 21.02-34.55 26.46-5.31 1.77-7.21 8.51-6.75 19.78.1 2.6.31 5.19.68 8.84.75 7.62.75 7.58.75 9.43 0 1.38-.42 2.73-1.24 4.09-1.33 2.2-3.26 4.07-7.94 8-6.25 5.24-8.36 7.12-11.67 10.63a68.8 68.8 0 0 0-12.25 17.71 58.8 58.8 0 0 0-5 17.36c-1.49 10.66-.85 22.09 1.13 33.15.43 2.37.88 4.53 1.33 6.44.16.66.3 1.25.6 4.06a249.3 249.3 0 0 1-1.17 16.12c-.37 3.37-.78 6.53-1.25 9.44zm-13.4 0h-1.05l.12-.28c3.07-7.16 4.29-11.83 4.29-18.72 0-3.57-.07-4.93-.76-15.65-.77-12.04-1-19.64-.55-28.3.58-11.5 2.4-22.1 5.81-32.16 1.3-3.8 2.8-7.5 4.55-11.1 3.46-7.14 6.83-12.39 10.42-16.6a59.02 59.02 0 0 1 4.35-4.56c.43-.4 3-2.8 3.67-3.45 5.72-5.6 7.51-11.52 7.51-29.18 0-18.84 2.9-23.77 15.82-28.24 1.09-.37 1.92-.67 2.77-.98a51.3 51.3 0 0 0 6.1-2.7c4.95-2.6 9.64-6.22 14.44-11.42 25.5-27.63 37.15-35.16 56.37-35.16 8.28 0 14.54-1.95 22-6.3 1.78-1.03 13.82-8.82 18.16-11.27 2.83-1.59 5.66-3.03 8.63-4.39 7.92-3.6 13.97-4.45 26.6-4.8 7.53-.2 10.7-.49 14.26-1.58 4.55-1.4 8.06-4 10.93-8.43 2.2-3.41 6.85-7.08 14.66-12.06 1.61-1.03 3.27-2.05 5.65-3.5 9.53-5.85 11.56-7.13 14.81-9.57 5.34-4 9.3-8.37 13.68-14.77a204.2 204.2 0 0 0 5.62-8.75v1.9c-1.97 3.17-3.4 5.38-4.8 7.42-4.42 6.48-8.46 10.92-13.9 15-3.29 2.46-5.32 3.75-14.89 9.61a375.06 375.06 0 0 0-5.63 3.5c-7.7 4.9-12.26 8.52-14.36 11.76-3 4.63-6.7 7.39-11.48 8.85-3.68 1.12-6.9 1.42-14.53 1.63-12.5.34-18.44 1.18-26.2 4.7a111.08 111.08 0 0 0-8.56 4.35c-4.3 2.43-16.34 10.22-18.15 11.27-7.6 4.43-14.03 6.43-22.5 6.43-18.87 0-30.3 7.4-55.63 34.84-4.88 5.28-9.67 8.97-14.7 11.62-2 1.05-4 1.92-6.23 2.75-.86.32-1.7.62-5.37 1.87-5.08 1.76-7.44 3.25-9.28 6.37-2.23 3.78-3.29 9.94-3.29 20.05 0 17.9-1.87 24.07-7.8 29.89-.69.67-3.27 3.06-3.69 3.46a58.04 58.04 0 0 0-4.28 4.49c-3.53 4.14-6.86 9.32-10.28 16.38a95.19 95.19 0 0 0-4.5 10.99c-3.38 9.97-5.18 20.48-5.76 31.9-.44 8.6-.22 16.17.55 28.17.69 10.76.76 12.12.76 15.72 0 6.35-1.02 10.87-4.35 19zm25.08 0h-1c-.04-4.73.06-9.39.28-15.02.26-6.41-.4-11.79-2.53-24.37l-.31-1.86c-2.12-12.55-2.76-19.35-1.97-26.47 1.03-9.25 4.75-16.68 12-22.67 22.04-18.2 29.81-30.18 29.81-44.61 0-2.6-.3-4.81-.98-8.17-.97-4.79-1.1-5.68-.97-7.57.2-2.56 1.27-4.7 3.56-6.72 2.67-2.35 7.05-4.6 13.72-7.01 9.72-3.5 15.52-9.18 24.3-21.57l1.78-2.5c4.48-6.33 7.1-9.63 10.43-12.78 4.31-4.07 8.98-6.77 14.54-8.17 13.3-3.32 20.37-5.47 25.34-7.64a49.5 49.5 0 0 0 5.28-2.7c1.1-.65 1.75-1.04 4.24-2.6 2.7-1.68 5.22-2.08 11.38-2.28 5.44-.18 7.9-.43 10.97-1.41a21.47 21.47 0 0 0 9.54-6.22c4.87-5.3 10.03-7.61 17.79-8.9 1.07-.18 1.88-.3 3.86-.58 6.9-.97 9.94-1.69 13.48-3.62 4.5-2.45 6.79-4.44 23.46-19.68l3.14-2.85c9.65-8.71 16.12-13.83 21.42-16.48 4.25-2.12 7.6-4.69 11.22-8.6v1.45c-3.42 3.57-6.69 6-10.78 8.05-5.18 2.59-11.61 7.67-21.2 16.32l-3.12 2.85c-16.8 15.35-19.05 17.3-23.66 19.82-3.68 2-6.8 2.75-13.82 3.73-1.97.28-2.78.4-3.84.57-7.56 1.26-12.52 3.48-17.21 8.6a22.47 22.47 0 0 1-9.97 6.5c-3.2 1-5.72 1.27-11.25 1.45-5.98.2-8.39.57-10.89 2.13a144 144 0 0 1-4.25 2.61 50.48 50.48 0 0 1-5.39 2.75c-5.04 2.2-12.15 4.37-25.5 7.7-9.74 2.44-15.26 7.65-24.4 20.56l-1.77 2.5c-8.9 12.54-14.82 18.34-24.78 21.93-6.57 2.36-10.85 4.57-13.4 6.82-2.1 1.86-3.05 3.74-3.22 6.04-.13 1.76 0 2.63.95 7.3.7 3.42 1 5.7 1 8.37 0 14.79-7.93 27-30.18 45.39-7.03 5.8-10.64 13-11.64 22-.78 7-.14 13.73 1.96 26.2l.32 1.85c2.15 12.65 2.8 18.07 2.54 24.58-.22 5.57-.32 10.2-.28 14.98zM95.9 600h-2.04c.68-3.82 1.14-8.8 1.61-15.98.2-3.11.27-4.06.39-5.6 1.3-17.54 4.04-27.14 11.5-33.2 4.65-3.77 7.22-8.92 8.67-16 .51-2.52.7-3.87 1.33-9.17.66-5.5 1.16-8.06 2.24-10.36 1.45-3.09 3.82-4.69 7.39-4.69 14.28 0 38.48 9.12 53.6 20.2 8.66 6.35 21.26 13.32 31.74 17.11 13.03 4.71 21.89 4.41 24.75-1.73 1.7-3.64 1.92-4.11 2.65-5.77 2.93-6.67 4.69-12.2 5.25-17.5.23-2.17.24-4.23.02-6.2-.32-2.75-1.42-4.55-4.08-7.35l-1.32-1.37a30.59 30.59 0 0 1-2.41-2.79 30.37 30.37 0 0 1-2.5-4.07l-1.13-2.14c-1.62-3.1-2.68-4.6-4.12-5.56-5.26-3.5-14.8-5.5-28.55-6.83a272.42 272.42 0 0 0-9.04-.71l-2.18-.17c-9.57-.73-15.12-1.56-19.06-3.2C156.57 471.07 136 450.5 136 440c0-5.34 1.74-9.53 5.47-14.13 1.98-2.44 11.12-11.71 12.79-13.54 4.52-4.97 10.16-9.54 17.68-14.66 2.8-1.9 14.78-9.6 17.49-11.49a50.54 50.54 0 0 0 6.34-5.43c1.53-1.5 6.96-7.13 7.12-7.3 7.18-7.3 12.7-11.56 19.74-14.38 3.36-1.34 8.13-2.79 17.45-5.38a9577.18 9577.18 0 0 1 11.78-3.28 602.6 602.6 0 0 0 12.67-3.7c20.4-6.24 34-12.08 40.79-18.44 8.74-8.2 11.78-13.84 15.73-26.02 2.02-6.22 3.09-9.04 5.07-12.72 9.54-17.71 28.71-39.37 43.5-45.45C383.77 238.25 389 232.34 389 226c0-2.89 2.73-8.4 6.83-13.73 4.76-6.2 10.65-11.36 16.75-14.18 12.5-5.77 33.5-10.09 47.42-10.09 5.32 0 9.83-1.5 16.42-4.89 9.2-4.71 10.1-5.11 13.58-5.11 10.42 0 32.06-2.55 45.76-5.97l3.88-.98 3.47-.89c2.6-.66 4.33-1.08 5.93-1.43 3.9-.86 6.76-1.23 9.58-1.17 2.74.06 5.47.52 8.67 1.48 4.56 1.37 13.71-.9 22.87-5.68a68.07 68.07 0 0 0 9.84-6.2v2.4c-11.09 8.14-25.76 13.66-33.29 11.4a29.72 29.72 0 0 0-8.13-1.4c-2.63-.05-5.36.3-9.11 1.12a238 238 0 0 0-9.33 2.3l-3.9.99C522.38 177.43 500.58 180 490 180c-2.99 0-3.91.4-12.67 4.89-6.85 3.51-11.61 5.11-17.33 5.11-13.65 0-34.35 4.26-46.58 9.9-5.78 2.67-11.42 7.62-16 13.58-3.85 5.02-6.42 10.2-6.42 12.52 0 7.27-5.8 13.82-20.62 19.92-14.27 5.88-33.16 27.21-42.5 44.55-1.9 3.55-2.95 6.28-4.93 12.4-4.05 12.47-7.23 18.39-16.27 26.86-7.08 6.64-20.87 12.57-41.57 18.89a604.52 604.52 0 0 1-12.7 3.71 1495.1 1495.1 0 0 1-11.8 3.28c-9.24 2.58-13.97 4.01-17.24 5.32-6.73 2.69-12.05 6.8-19.05 13.92-.15.15-5.6 5.8-7.15 7.32a52.4 52.4 0 0 1-6.6 5.65c-2.74 1.92-14.75 9.63-17.5 11.5-7.4 5.04-12.94 9.52-17.33 14.35-1.72 1.9-10.8 11.11-12.71 13.46-3.47 4.26-5.03 8.03-5.03 12.87 0 9.5 20 29.5 33.38 35.08 3.67 1.53 9.1 2.34 18.45 3.05a586.23 586.23 0 0 0 4.34.32c3.24.23 5.07.37 6.93.55 14.08 1.37 23.82 3.4 29.45 7.17 1.82 1.2 3.02 2.91 4.8 6.29l1.11 2.13a28.55 28.55 0 0 0 2.34 3.81c.62.83 1.3 1.6 2.26 2.61.23.24 1.1 1.16 1.32 1.37 2.93 3.09 4.24 5.23 4.61 8.5.24 2.12.23 4.33-.01 6.64-.59 5.55-2.4 11.25-5.41 18.1-.74 1.67-.96 2.15-2.66 5.8-3.49 7.47-13.33 7.8-27.25 2.77-10.67-3.86-23.43-10.92-32.25-17.38C164.62 515.96 140.82 507 127 507c-5 0-6.4 3.02-7.64 13.29a99.03 99.03 0 0 1-1.36 9.33c-1.53 7.5-4.3 13.04-9.37 17.16-6.87 5.58-9.5 14.78-10.77 31.8-.11 1.52-.18 2.47-.38 5.57-.46 7.01-.91 11.99-1.57 15.85zm8.05 0h-1.02c.29-1.41.58-2.94.9-4.59l1.05-5.62c2.5-13.3 4.2-19.92 6.68-24.05 1.7-2.84 3.68-5.5 8.05-11.03 8.21-10.36 10.88-14.55 10.88-18.71l-.02-1.69c-.02-1.78-.02-2.7.02-3.77.21-5.05 1.47-8.2 4.64-9.4 3.92-1.5 10.39.44 20.12 6.43 9.56 5.88 17.53 10.7 25.91 15.66 1.31.78 14.27 8.41 17.67 10.45a714.21 714.21 0 0 1 6.42 3.9c13.82 8.5 38.94 5.05 46.3-7.83 3.6-6.28 4.54-8.52 7.78-17.32a82.3 82.3 0 0 1 1.18-3.07 42.27 42.27 0 0 1 4.06-7.64c9.33-13.98 14.92-26.1 14.92-36.72 0-3.66.75-6.62 3.36-14.85.52-1.64.83-2.66 1.15-3.73 3.64-12.23 3.04-19.12-4.29-24a23.1 23.1 0 0 0-9.98-3.78c-7.2-.93-14.49 1.17-23.91 5.88-1.55.78-6.64 3.44-7.6 3.93a62.6 62.6 0 0 0-4.14 2.3l-4.4 2.66c-11.62 6.92-20.4 9.18-32.81 6.08-3.32-.84-6.24-1.4-13.1-2.64-13.25-2.39-18.7-3.75-23.33-6.46-6.23-3.67-7.46-9.02-2.88-16.65A93.1 93.1 0 0 1 172 415.42a157 157 0 0 1 8.32-7.66c-.07.05 6.16-5.3 7.82-6.77a85.12 85.12 0 0 0 6.5-6.33c7.7-8.46 12.78-13.36 20.08-18.57 9.94-7.1 21.4-12.36 35.18-15.58 37.03-8.64 51-12.7 58.83-17.93 8.6-5.73 21.3-24.77 36.84-54.81 5.22-10.1 12.27-18.4 21.13-25.71 5.13-4.24 9.56-7.25 17.55-12.23 7.42-4.62 9.62-6.14 11.38-8.16a21.15 21.15 0 0 0 2.95-4.87c.61-1.3 2.87-6.47 3-6.77 1.36-3 2.56-5.4 3.95-7.73 6.53-10.97 16.03-18 31.4-20.8 12.73-2.3 19.85-2.7 29.68-2.3 3.25.13 4.13.16 5.6.14 5.15-.07 9.71-1.04 16.61-3.8 20.74-8.3 38.75-12.04 59.19-12.04 3.05 0 6.03.15 10.48.48l2.09.16c12.45.96 18.08.96 25.34-.63a49.65 49.65 0 0 0 14.09-5.45v1.15a50.52 50.52 0 0 1-13.88 5.28c-7.38 1.61-13.08 1.61-25.63.65l-2.08-.16c-4.43-.33-7.39-.48-10.41-.48-20.3 0-38.2 3.72-58.81 11.96-7.01 2.8-11.7 3.8-16.97 3.88-1.5.02-2.39-.01-5.66-.14-9.76-.4-16.8-.01-29.47 2.3-15.06 2.73-24.32 9.58-30.71 20.31a72.8 72.8 0 0 0-3.9 7.63c-.12.28-2.39 5.47-3.01 6.79a22 22 0 0 1-3.1 5.1c-1.86 2.13-4.07 3.66-11.6 8.35-7.95 4.96-12.35 7.95-17.44 12.15-8.76 7.23-15.73 15.43-20.89 25.4-15.61 30.2-28.36 49.32-37.16 55.19-7.98 5.32-21.97 9.39-59.17 18.07-13.65 3.18-24.98 8.39-34.82 15.42-7.22 5.16-12.27 10.01-19.92 18.43a86.07 86.07 0 0 1-6.57 6.4c-1.67 1.48-7.91 6.83-7.84 6.77-3.27 2.84-5.8 5.16-8.26 7.62a92.1 92.1 0 0 0-14.27 18.13c-4.3 7.16-3.22 11.89 2.53 15.26 4.47 2.63 9.88 3.99 23.24 6.39a185.7 185.7 0 0 1 12.92 2.6c12.11 3.03 20.64.84 32.06-5.96l4.4-2.65c1.66-1 2.96-1.73 4.2-2.35.95-.48 6.04-3.14 7.6-3.92 9.59-4.8 17.04-6.94 24.49-5.98a24.1 24.1 0 0 1 10.4 3.93c7.82 5.21 8.45 12.52 4.7 25.13-.32 1.07-.64 2.1-1.16 3.74-2.57 8.12-3.31 11.04-3.31 14.55 0 10.88-5.66 23.14-15.08 37.28a41.28 41.28 0 0 0-3.97 7.46c-.37.9-.73 1.82-1.18 3.04-3.25 8.85-4.21 11.13-7.84 17.47-7.67 13.42-33.43 16.95-47.7 8.18a578.4 578.4 0 0 0-6.4-3.89c-3.4-2.04-16.36-9.67-17.67-10.45-8.38-4.97-16.36-9.78-25.92-15.66-9.5-5.85-15.7-7.7-19.24-6.36-2.68 1.02-3.8 3.82-4 8.51a61.12 61.12 0 0 0-.02 3.72l.02 1.7c0 4.5-2.69 8.73-11.52 19.87-3.92 4.95-5.87 7.59-7.55 10.39-2.39 3.97-4.08 10.56-6.56 23.72l-1.05 5.62-.86 4.4zm10.5 0h-1c.03-.34.04-.68.04-1 0-12.39 8.48-33.57 19.16-43.37a26.18 26.18 0 0 0 3.67-4.17 35.8 35.8 0 0 0 2.88-4.9c.36-.72 1.75-3.66 2.1-4.36 3.22-6.29 6.84-6.54 16.97.39 1.34.9 6.07 4.16 6.4 4.38 2.62 1.8 4.67 3.2 6.7 4.56 5.03 3.39 9.37 6.2 13.51 8.7 14.33 8.67 25.49 13.27 34.11 13.27 16.86 0 32.71-5.95 39.6-14.8 1.59-2.04 3.2-5.17 5.06-9.63.8-1.92 1.64-4.06 2.67-6.8l2.74-7.33c4.66-12.44 7.76-19.06 11.56-23.27 7.9-8.79 14.87-36 14.87-52.67 0-1.9.17-3.11 1.02-8.27.37-2.2.58-3.6.74-5.07.63-5.51.21-9.46-1.68-12.39-4.6-7.1-19.7-9.23-38.46-4.78a100.57 100.57 0 0 0-18.94 6.3c-5.17 2.37-17.11 9.74-16.5 9.4-6.72 3.64-12.97 4.15-24.8 1.3-29.55-7.14-30.43-8.62-15.26-26.81 17.44-20.93 47.12-46.18 56.38-46.18 9.92 0 53.84-11.98 65.78-17.95 9.46-4.73 24.32-21.18 36.82-37.85.71-.95 13.5-21.6 19.2-29.6 9.35-13.13 18.22-22.55 26.95-27.53 7.29-4.17 13.16-10.28 18.8-18.73 1.93-2.9 10.52-17.65 12.73-20.41 1.54-1.93 3-3.21 4.52-3.89 14.07-6.25 24.22-9.04 39.2-9.04h29c4.05 0 7.36-.4 22.93-2.5l4.3-.57c9.92-1.3 16.57-1.93 21.77-1.93 1.66 0 2.95.01 6.03.04 18.61.19 28.55-.48 44.86-4.03 3.1-.67 6.13-1.78 9.11-3.31v1.12a37.96 37.96 0 0 1-8.9 3.17c-16.4 3.56-26.4 4.24-45.08 4.05-3.08-.03-4.36-.04-6.02-.04-5.15 0-11.76.63-21.64 1.92l-4.3.58c-15.64 2.11-18.94 2.5-23.06 2.5h-29c-14.81 0-24.84 2.75-38.8 8.96-1.34.6-2.69 1.78-4.14 3.6-2.16 2.68-10.72 17.39-12.68 20.33-5.72 8.57-11.7 14.8-19.13 19.04-8.57 4.9-17.36 14.23-26.63 27.24-5.68 7.97-18.47 28.64-19.22 29.63-12.6 16.8-27.52 33.32-37.18 38.15-12.06 6.03-56.14 18.05-66.22 18.05-8.82 0-38.39 25.15-55.62 45.82-14.6 17.52-14.19 18.21 14.74 25.2 11.6 2.8 17.6 2.3 24.09-1.2-.67.35 11.31-7.03 16.56-9.44 5.41-2.48 11.6-4.59 19.11-6.37 19.13-4.53 34.65-2.35 39.54 5.22 2.05 3.17 2.48 7.32 1.84 13.04a96.34 96.34 0 0 1-.75 5.13c-.84 5.08-1.01 6.29-1.01 8.1 0 16.9-7.03 44.33-15.13 53.33-3.68 4.09-6.76 10.65-11.37 22.96-.35.93-2.2 5.94-2.73 7.33-1.04 2.76-1.88 4.9-2.68 6.84-1.9 4.53-3.55 7.73-5.2 9.85-7.1 9.13-23.25 15.19-40.39 15.19-8.86 0-20.15-4.65-34.63-13.42-4.15-2.51-8.5-5.32-13.55-8.72a861.54 861.54 0 0 1-6.71-4.56l-6.4-4.39c-9.68-6.63-12.61-6.42-15.5-.75-.35.68-1.74 3.62-2.1 4.35a36.77 36.77 0 0 1-2.96 5.03c-1.12 1.57-2.37 3-3.81 4.33-10.47 9.6-18.84 30.51-18.84 42.63l-.03 1zm-29.65 0h-1.1c1.17-2.52 1.79-5.2 1.79-8 0-20 4.83-42.04 12.15-49.35 5.17-5.18 7.77-8.38 9.9-12.74 2.64-5.41 3.95-12 3.95-20.91 0-6.82 1.14-11.59 3.37-15.07 1.74-2.7 3.6-4.21 8.91-7.52a31.64 31.64 0 0 0 3.9-2.79c4.61-3.96 6.58-6.2 7.72-9.41 1.43-4.02.93-9.04-1.86-16.02a68.98 68.98 0 0 0-3.99-8.07l-.93-1.7a75.47 75.47 0 0 1-2.64-5c-5.16-10.71-3.77-18.9 7.68-29.78a204 204 0 0 1 26.81-21.55c3.96-2.69 16.8-10.8 19.24-12.5 1.99-1.4 4.33-3.3 7.77-6.3-.02 0 7.23-6.39 9.47-8.3 4.97-4.26 9.09-7.5 13.05-10.15 4.72-3.15 8.97-5.28 12.87-6.32 12.78-3.41 15.6-4.18 21.77-5.97 12.55-3.64 21.96-6.9 28.14-10a45.47 45.47 0 0 1 7.47-2.79c8.66-2.66 12.02-4.1 16.97-8.1 6.78-5.46 13.07-14.25 19.33-27.87 15.97-34.77 19.08-39.39 32.15-49.19 3.14-2.36 6.37-4.1 11.43-6.4l2.33-1.04c11.93-5.35 16.87-8.93 21.1-17.38 1.88-3.77 2.48-6.29 3.37-12.27.78-5.19 1.48-7.56 3.53-10.25 2.57-3.4 7.03-6.27 14.36-9.01 3.37-1.26 7.36-2.5 12.05-3.73 16.33-4.3 25.28-5.36 39.6-5.81 6.9-.22 9.5-.56 12.66-2 1.19-.54 2.36-1.23 3.58-2.11 3.7-2.7 8.14-4.54 13.24-5.67 5.71-1.27 10.69-1.54 18.7-1.45l2.35.02c2.82 0 6.8-1 19.7-4.69 10.83-3.08 15.95-4.31 19.3-4.31.82 0 1.9.13 3.55.41l5.01.9c9.82 1.68 17.44 1.89 25.15-.21 7.98-2.18 14.8-6.77 20.29-14.24V147c-5.47 7.04-12.21 11.42-20.03 13.55-7.88 2.15-15.63 1.94-25.58.23l-5-.9c-1.6-.26-2.64-.39-3.39-.39-3.2 0-8.32 1.22-19.74 4.48-12.35 3.53-16.3 4.52-19.26 4.52l-2.36-.02c-7.94-.1-12.85.17-18.47 1.42-4.97 1.11-9.3 2.9-12.88 5.5a21.4 21.4 0 0 1-3.75 2.22c-3.32 1.5-6 1.87-13.04 2.09-14.25.44-23.13 1.5-39.37 5.77a125.56 125.56 0 0 0-11.95 3.7c-7.17 2.7-11.49 5.46-13.93 8.68-1.9 2.52-2.58 4.76-3.33 9.8-.9 6.08-1.53 8.68-3.47 12.56a30.6 30.6 0 0 1-9.66 11.45c-3.12 2.26-5.95 3.73-11.93 6.4l-2.31 1.04c-5.01 2.27-8.18 3.99-11.25 6.29-12.9 9.68-15.93 14.17-31.85 48.8-6.31 13.76-12.7 22.68-19.6 28.25-5.08 4.1-8.53 5.57-17.3 8.27a44.64 44.64 0 0 0-7.33 2.73c-6.24 3.12-15.7 6.4-28.3 10.06a867.4 867.4 0 0 1-21.8 5.97c-3.77 1.01-7.93 3.1-12.56 6.19a137.35 137.35 0 0 0-12.95 10.07c-2.24 1.92-9.48 8.3-9.48 8.3a98.2 98.2 0 0 1-7.84 6.37c-2.46 1.72-15.32 9.83-19.26 12.5a203 203 0 0 0-26.69 21.45c-11.13 10.58-12.43 18.3-7.47 28.63a74.52 74.52 0 0 0 2.62 4.95l.94 1.7a69.84 69.84 0 0 1 4.03 8.17c2.88 7.2 3.4 12.46 1.89 16.73-1.22 3.43-3.28 5.77-8.02 9.84-1.14.97-2.32 1.8-5.3 3.67-3.92 2.45-5.69 3.89-7.31 6.42-2.13 3.3-3.22 7.89-3.22 14.53 0 9.05-1.34 15.79-4.05 21.34-2.19 4.49-4.85 7.77-10.1 13.01-7.07 7.07-11.85 28.9-11.85 48.65 0 2.8-.58 5.48-1.7 8zm282.54 0h-1.01l-1.1-5.8c-3.08-16.26-4.05-26.2-2.74-37.26.7-5.8.77-9.68.55-15.3-.18-4.45-.17-5.68.19-7.63.78-4.3 3.44-8.53 10.39-16.34 9.07-10.2 12.26-15.41 19.8-30.15 1.35-2.64 2.33-4.47 3.38-6.3.9-1.58 1.82-3.06 2.77-4.5 3.14-4.7 7.03-8.42 16.84-16.81 11.22-9.6 15.5-13.86 18.13-19.13.7-1.4 1.3-2.8 1.93-4.4a206 206 0 0 0 1.49-4.05c3.63-9.94 8.01-13.93 22.9-17.81 4.99-1.3 20.55-5.13 21.38-5.34 16.19-4.1 25.33-7.36 33.48-12.6 5.86-3.77 5.84-3.76 27.66-16.53l2.6-1.52c10.23-6 17.1-10.2 22.73-13.95a149.3 149.3 0 0 0 8.8-6.3 723.7 723.7 0 0 0 6.37-5.08A87.74 87.74 0 0 1 600 342.95v1.12a85.76 85.76 0 0 0-15.49 9.9c.18-.14-4.76 3.84-6.38 5.1a150.3 150.3 0 0 1-8.85 6.35c-5.65 3.76-12.53 7.96-22.78 13.97l-2.6 1.53c-21.8 12.75-21.78 12.74-27.63 16.5-8.27 5.32-17.49 8.61-33.78 12.73-.83.21-16.39 4.04-21.36 5.33-8.03 2.1-13.15 4.5-16.45 7.5-2.66 2.42-4 4.86-5.77 9.7l-1.5 4.07a51.12 51.12 0 0 1-1.96 4.47c-2.72 5.45-7.04 9.75-18.38 19.45-9.73 8.32-13.6 12.02-16.65 16.6a77.18 77.18 0 0 0-2.74 4.45c-1.05 1.81-2.01 3.63-3.35 6.25-7.58 14.81-10.82 20.08-19.96 30.36-6.83 7.7-9.4 11.78-10.15 15.86-.34 1.85-.34 3.04-.17 7.4.22 5.68.14 9.6-.55 15.47-1.3 10.92-.34 20.79 2.73 36.95l1.12 5.99zm-76.59 0h-2.1l1.39-4.3c1.04-3.3 1.93-6.78 2.68-10.4 2.65-12.73 3.27-23.63 3.27-41.3 0-5.71-1.86-9.75-4.13-9.75-2.94 0-6.96 5.61-10.93 17.08C271.14 579.68 258.3 593 238 593c-22.42 0-29.26-1.35-48.42-10.09a87.69 87.69 0 0 1-9.42-5.04c-2.95-1.8-12.78-8.57-14.84-9.72-4.2-2.36-7-2.71-9.72-.99-.63.4-1.26.91-1.9 1.55a57.69 57.69 0 0 1-4.31 3.86 147.88 147.88 0 0 1-3.06 2.44l-1 .8C137.01 582.43 134 587.18 134 597c0 1.02-.02 2.01-.07 3h-2c.05-.99.07-1.98.07-3 0-10.52 3.33-15.78 12.09-22.76a265.61 265.61 0 0 1 2-1.6c.83-.64 1.43-1.13 2.03-1.61a55.76 55.76 0 0 0 4.17-3.74c.74-.73 1.48-1.34 2.24-1.82 3.47-2.2 7-1.75 11.77.93 2.15 1.21 12.03 8 14.9 9.76a85.7 85.7 0 0 0 9.22 4.93C209.29 589.7 215.85 591 238 591c19.25 0 31.49-12.7 41.06-40.33 4.24-12.25 8.66-18.42 12.81-18.42 3.8 0 6.13 5.06 6.13 11.75 0 17.8-.63 28.8-3.3 41.7-.77 3.7-1.68 7.23-2.75 10.6-.4 1.3-.8 2.53-1.19 3.7zm-149.25 0l.5-.94a160.1 160.1 0 0 0 6.53-13.26c2.73-6.29 5.78-9.64 9.24-10.52 3.74-.95 7.15.74 12.56 5.13 5.43 4.4 6.07 4.86 7.73 5.1 1.6.22 4.28 1.14 8.86 2.95 1.3.5 10.78 4.35 13.85 5.55 3.07 1.2 5.85 2.25 8.49 3.18 3.1 1.1 5.98 2.04 8.65 2.81h-3.45c-1.76-.56-3.6-1.18-5.54-1.87a281.2 281.2 0 0 1-8.51-3.19c-3.08-1.2-12.57-5.04-13.86-5.55-4.5-1.78-7.15-2.68-8.63-2.9-1.94-.27-2.53-.7-8.22-5.3-5.17-4.2-8.36-5.78-11.69-4.94-3.1.78-5.94 3.92-8.56 9.95a161 161 0 0 1-6.82 13.8h-1.13zm112.89 0a30.34 30.34 0 0 0 11.27-6.27c1.55-1.36 3.32-3.46 5.34-6.29 1.05-1.46 2.15-3.1 3.41-5.04a349.73 349.73 0 0 0 2.5-3.9l.47-.75.93-1.47a89.17 89.17 0 0 1 3.25-4.86c1.05-1.43 1.82-2.23 2.44-2.46 1.02-.37 1.49.48 1.49 2.04l.01 2.11c.05 6.91-.08 11.32-.7 16.33a48.4 48.4 0 0 1-2.38 10.56h-1.07a46.47 46.47 0 0 0 2.45-10.68c.62-4.96.75-9.33.7-16.2l-.01-2.12c0-.97-.08-1.12-.15-1.1-.36.14-1.05.85-1.97 2.1a88.44 88.44 0 0 0-3.22 4.82l-.92 1.46-.48.75a1268.1 1268.1 0 0 1-2.5 3.92c-1.26 1.95-2.38 3.6-3.44 5.08-2.06 2.88-3.87 5.04-5.5 6.45a30.87 30.87 0 0 1-8.94 5.52h-2.98zm-183.72 0H69.3c3.37-3.43 5.19-8.33 5.19-15 0-18.6-.04-17.35 1.02-20.77.6-1.93 1.5-3.74 3.27-6.63.42-.7 4.92-7.8 6.78-10.86 3.04-4.97 11.04-16.5 12.21-18.56 3.48-6.08 4.72-12.06 4.72-24.18 0-7.85 2.5-14.2 8.1-23.44l2.84-4.63a72.67 72.67 0 0 0 2.49-4.4c1.62-3.15 2.48-5.78 2.62-8.28.2-3.78-1.3-7.29-4.9-10.9-5.13-5.12-8.6-5.43-11.2-1.85-2.12 2.92-3.48 7.74-5.06 16.47-.2 1.03-.82 4.6-.82 4.57-.83 4.67-1.4 7.33-2.1 9.6-1.35 4.42-3.7 7.61-8.36 12.26l-3.26 3.2c-6.38 6.39-9.68 11.51-11.36 19.5l-1.16 5.52c-.87 4.1-1.56 7.04-2.33 9.94-3.67 13.74-9.65 25.97-22.59 44.72-7.68 11.14-11.05 18.87-10.92 23.72h-1c-.12-5.16 3.35-13.05 11.1-24.28 12.87-18.67 18.8-30.8 22.44-44.42.77-2.88 1.45-5.8 2.32-9.89l1.16-5.51c1.73-8.22 5.13-13.5 11.64-20 .63-.64 2.84-2.8 3.25-3.21 4.57-4.54 6.82-7.62 8.12-11.84a81.58 81.58 0 0 0 2.07-9.48l.81-4.57c1.62-8.9 3-13.8 5.24-16.89 3-4.15 7.2-3.78 12.71 1.74 3.8 3.8 5.42 7.58 5.2 11.66-.15 2.66-1.05 5.41-2.73 8.68a73.6 73.6 0 0 1-2.52 4.46l-2.84 4.63c-5.52 9.1-7.96 15.3-7.96 22.92 0 12.28-1.28 18.43-4.85 24.68-1.2 2.1-9.21 13.65-12.22 18.58-1.87 3.06-6.37 10.18-6.78 10.86-1.73 2.82-2.6 4.57-3.17 6.4-1.02 3.28-.98 2.1-.98 20.48 0 6.52-1.7 11.44-4.82 15zM310.09 0h1.06c-.37.9-.77 1.83-1.2 2.82-3.9 9.06-5.45 15.15-5.45 25.18 0 7.64-2.1 11.6-6.64 13.05-3.46 1.1-5.72.98-17.57-.43-11.55-1.36-19.17-1.58-28.16-.14-6.24 2.49-25.91 7.02-32.13 7.02-11.15 0-36.76-2.88-54.12-7.01a22.08 22.08 0 0 0-16.95 2.48c-4.05 2.33-7.09 5.03-13.9 11.97-6.28 6.39-9.53 9.23-13.8 11.5-7.09 3.79-11.22 7.65-13.4 12.27-1.82 3.85-2.33 7.84-2.33 15.29 0 4.4-2.65 6.69-9.45 9.74.1-.05-2.97 1.31-3.84 1.71-8.78 4.06-12.71 8.29-12.71 16.55 0 12.52-4.86 19.22-17.34 27.96l-4.56 3.14c-1.9 1.3-3.3 2.3-4.67 3.3-.92.68-1.79 1.34-2.62 2-7.16 5.62-11 14.54-15.56 33.28-.63 2.57-3.3 14-4.07 17.14a350.44 350.44 0 0 1-5.2 19.33c-1.37 4.5-4.5 15.07-4.96 16.53-1.05 3.4-1.64 4.94-2.46 6.32-.82 1.4-6.85 9.08-12.64 18.27L0 277.98v-1.9l4.58-7.35a270.8 270.8 0 0 1 12.61-18.23c-.3.5 1.35-2.8 2.38-6.12.45-1.44 3.58-12.01 4.95-16.53 1.83-6.03 3.44-12.09 5.19-19.27.76-3.13 3.44-14.56 4.06-17.14 4.62-18.95 8.52-28.02 15.92-33.83.84-.67 1.72-1.33 2.65-2.01 1.38-1.02 2.8-2.01 4.7-3.32l4.54-3.14C73.83 140.57 78.5 134.13 78.5 122c0-8.74 4.2-13.26 13.29-17.45.88-.41 3.96-1.77 3.85-1.73 6.46-2.9 8.86-4.97 8.86-8.82 0-7.6.53-11.7 2.42-15.71 2.29-4.84 6.57-8.85 13.84-12.73 4.15-2.21 7.35-5 14.15-11.93 6.28-6.4 9.36-9.13 13.52-11.53a23.07 23.07 0 0 1 17.69-2.59c17.27 4.12 42.8 6.99 53.88 6.99 6.1 0 25.73-4.53 31.92-7 9.12-1.46 16.83-1.25 28.49.13 11.63 1.38 13.9 1.5 17.15.47 4.06-1.3 5.94-4.85 5.94-12.1 0-10.1 1.56-16.3 6.6-28zm25.12 0h1c.05 5.62.26 11.48.65 19.4.47 9.7.64 14.57.64 21.6 0 9.81-4.68 17.46-13.1 23.16-6.53 4.43-14.94 7.46-24.33 9.33-3.74.54-9.42.56-22.68.23-6.74-.17-9.35-.22-12.39-.22-2.77 0-4.97.43-7.63 1.36-.88.3-4.55 1.74-5.58 2.11-6.55 2.35-13.59 3.53-24.79 3.53-8.1 0-13.58-1.38-22.46-4.9l-3.18-1.25c-12.55-4.87-21.27-5.15-37.18 1.12-11.15 4.39-18.13 9.2-22.28 14.81-3.15 4.26-4.33 7.8-5.94 15.8-1.22 6.09-1.93 8.74-3.5 12.13-1.65 3.53-3.97 5.81-7.07 7.22-2.33 1.07-4.35 1.5-9.32 2.19-9.04 1.27-12.77 3.09-15.61 9.58-3.71 8.48-7.72 13.87-14.22 19.76-2.4 2.18-13.14 11.02-15.91 13.42-8.2 7.1-13.85 17.37-18.7 31.97a258.81 258.81 0 0 0-3.27 10.7c-.01.05-2.26 7.97-2.88 10.1-8.49 28.85-17.88 52.95-26.13 61.2-2.8 2.8-5.06 5.64-10.4 12.96-3.4 4.68-6.23 8.25-8.95 11.1v-1.55c2.74-2.98 5.73-6.82 9.48-11.97 4.03-5.52 6.32-8.4 9.17-11.24 8.07-8.08 17.44-32.14 25.87-60.8.62-2.1 2.86-10.03 2.88-10.08 1.21-4.24 2.21-7.53 3.28-10.74 4.9-14.75 10.63-25.16 19-32.4 2.78-2.42 13.5-11.25 15.89-13.4 6.4-5.8 10.32-11.09 13.97-19.43 1.68-3.83 4.05-6.31 7.2-7.86 2.4-1.17 4.64-1.67 9.53-2.36 4.54-.63 6.5-1.05 8.7-2.06 2.89-1.31 5.03-3.42 6.58-6.73 1.53-3.3 2.23-5.9 3.43-11.9 1.64-8.14 2.85-11.79 6.11-16.2 4.28-5.79 11.41-10.7 22.73-15.16 16.15-6.36 25.13-6.07 37.9-1.11l3.19 1.26c8.77 3.47 14.13 4.82 22.09 4.82 11.09 0 18.02-1.16 24.46-3.47 1-.36 4.68-1.8 5.58-2.11A22.5 22.5 0 0 1 265 72.5c3.05 0 5.67.05 14.07.26 11.53.29 17.2.27 20.83-.25 9.25-1.85 17.54-4.83 23.94-9.17C332 57.8 336.5 50.46 336.5 41c0-7-.17-11.86-.7-22.7-.35-7.26-.55-12.83-.59-18.3zM93.87 0h2.04c-.7 4-1.61 6.82-3.03 9.47-2.33 4.38-2.85 5.75-5.26 13.03a40.46 40.46 0 0 1-1.94 5.03c-2.24 4.66-5.92 8.8-13.07 14.26-8.01 6.13-14.27 16.55-20.03 31.55-2.4 6.23-8.75 25.63-9.64 28.01-2.69 7.16-6.56 12.7-15.63 23.68l-2.68 3.24c-6.02 7.34-9.35 12.07-11.72 17.15-2.3 4.94-7.12 9.9-12.91 14.15v-2.4c5.14-3.94 9.1-8.3 11.1-12.6 2.46-5.27 5.87-10.1 11.98-17.56l2.68-3.26c8.94-10.8 12.72-16.22 15.3-23.1.88-2.33 7.24-21.74 9.65-28.03 5.89-15.31 12.3-26 20.68-32.41 6.92-5.3 10.4-9.2 12.48-13.55.65-1.35 1.16-2.7 1.85-4.79 2.45-7.4 3-8.83 5.4-13.34A27.68 27.68 0 0 0 93.87 0zm9.07 0h1.02c-1.66 8.3-2.91 12.67-4.54 15.26a59.14 59.14 0 0 0-4.1 8.21c-1.27 3-2.44 6.2-3.5 9.4-.38 1.12-.7 2.16-2.41 5.39a251.48 251.48 0 0 0-12.81 13.3c-3.48 3.96-5.95 7.27-7.15 9.66-.95 1.9-2.06 5.99-3.61 12.97-.64 2.9-3.65 17.15-4.51 21.07-3.63 16.45-6.63 26.69-9.9 32-7.66 12.45-10.64 15.71-37.08 41.1A69.78 69.78 0 0 1 0 179.21v-1.15a69.39 69.39 0 0 0 13.65-10.42c26.4-25.33 29.32-28.55 36.92-40.9 3.2-5.18 6.18-15.37 9.78-31.7.86-3.91 3.87-18.16 4.51-21.06 1.57-7.09 2.7-11.2 3.7-13.2 1.24-2.5 3.76-5.86 7.29-9.89.9-1.03 1.86-2.1 2.86-3.18 2.4-2.6 4.96-5.22 7.53-7.76.9-.88 1.73-1.7 3.37-3.4a129.02 129.02 0 0 1 4.78-13.46 60.07 60.07 0 0 1 4.19-8.35c1.52-2.44 2.74-6.71 4.36-14.74zM83.71 0h1.1c-2.09 4.74-6.03 8.92-11.42 12.3-7.2 4.52-16.5 7.2-24.39 7.2-8.9 0-11.8 7-11.74 21.52 0 1.7.04 3.17.12 5.99.1 3.3.12 4.45.12 5.99 0 5.73-.76 11.3-2.01 16.5a66.67 66.67 0 0 1-2.15 6.97 2597.76 2597.76 0 0 1-7 15.86A4270.8 4270.8 0 0 1 6.44 136.2 54.64 54.64 0 0 1 0 147v-1.65a54.87 54.87 0 0 0 5.55-9.57A4269.82 4269.82 0 0 0 30.7 79.97c.53-1.2.99-2.23 2.44-5.9A69.23 69.23 0 0 0 36.5 53c0-1.52-.03-2.66-.12-5.95-.08-2.83-.12-4.31-.12-6.01-.03-6.79.53-11.62 2.07-15.34 1.94-4.68 5.39-7.19 10.67-7.19 7.7 0 16.81-2.63 23.86-7.05C77.93 8.27 81.66 4.38 83.7 0zm282.63 0h1.01c1.86 10.02 2.18 12.67 2.32 18.3a123.43 123.43 0 0 1 .37 27.83c-.96 8.78-3.1 16.01-6.63 21.15-11.34 16.5-39.8 29.22-66.41 29.22-5.09 0-10.47.28-16.31.83a413.8 413.8 0 0 0-24.37 3.16c-21.56 3.26-27.66 4.01-36.32 4.01-6.92 0-12.2-1.05-21.69-3.9l-2.78-.83c-1.39-.41-2.54-.74-3.65-1.02-8-2.05-14.22-2.04-21.7.72a16.32 16.32 0 0 0-9.17 8.18c-1.6 3.05-2.5 6.06-4.02 12.83-1.5 6.64-2.34 9.52-3.99 12.64a16.16 16.16 0 0 1-9.85 8.36 104.8 104.8 0 0 0-9.5 3.42c-6.55 2.8-10.1 5.57-13.8 10.47-1.33 1.75-1.03 1.3-5.43 7.9-1.98 2.97-4.66 5.8-8.48 9.14-2.01 1.76-10.71 8.83-12.88 10.7-7.37 6.35-12.58 12.14-16.63 19.14-4.22 7.3-7.8 18.3-11.28 33.26-.87 3.73-1.72 7.64-2.64 12.14l-1.18 5.8-1.09 5.45c-1.8 8.96-2.77 13.28-3.77 16.26-6.8 20.44-17.26 42.16-27.13 51.2-5.11 4.7-8.1 7.07-11.1 8.86-.9.54-1.84 1.04-2.92 1.57-.44.22-9.6 4.4-14.1 6.66l-1.22.62v-1.13l.78-.39c4.52-2.26 13.67-6.44 14.1-6.65a41.19 41.19 0 0 0 2.84-1.54c2.94-1.75 5.88-4.09 10.94-8.73 9.71-8.9 20.1-30.51 26.87-50.79.97-2.92 1.94-7.22 3.73-16.13l1.1-5.46a490.5 490.5 0 0 1 3.82-17.96c3.5-15.06 7.1-26.14 11.39-33.54 4.11-7.11 9.4-12.98 16.83-19.4 2.19-1.88 10.88-8.95 12.88-10.7 3.77-3.28 6.39-6.05 8.3-8.93 4.43-6.64 4.12-6.18 5.47-7.96 3.8-5.03 7.5-7.91 14.21-10.78 2.61-1.12 5.74-2.24 9.59-3.46a15.17 15.17 0 0 0 9.27-7.86c1.59-3.02 2.42-5.85 4.03-12.99 1.41-6.27 2.32-9.33 3.98-12.48a17.31 17.31 0 0 1 9.7-8.66c7.7-2.83 14.1-2.84 22.3-.75 1.12.29 2.28.61 3.68 1.03l3.73 1.11c8.47 2.54 13.66 3.58 20.46 3.58 8.59 0 14.67-.75 36.18-4a414.64 414.64 0 0 1 24.41-3.17c5.88-.54 11.29-.83 16.41-.83 26.3 0 54.45-12.58 65.59-28.78 3.42-4.98 5.5-12.06 6.46-20.7.84-7.74.73-16.02.02-23.9a136.2 136.2 0 0 0-.57-5.12c0-4.47-.3-6.94-2.16-17zM18.88 0h1.03C18 7.57 17.15 10.18 14.46 16.2c-1.95 4.37-2.67 9.19-2.42 14.89.2 4.33.71 7.7 2.28 16.13 1.09 5.88 1.57 8.77 1.94 12.2.96 8.9.24 16.08-2.8 22.79A463.4 463.4 0 0 1 0 109.43v-2.12a465 465 0 0 0 12.54-25.52c2.97-6.52 3.67-13.53 2.72-22.27-.36-3.4-.84-6.26-1.93-12.12-1.57-8.47-2.1-11.88-2.29-16.27-.26-5.84.48-10.81 2.5-15.33 2.64-5.9 3.48-8.47 5.34-15.8zm280.47 0a70.78 70.78 0 0 1-4.91 11.24c-2.56 4.7-4.01 8.45-4.86 11.98l-.4 1.8-.28 1.45a5.28 5.28 0 0 1-.74 2.07c-.74 1.03-1.93 1.28-5.13 1.25.92 0-9.85-.29-15.03-.29-10.2 0-18.45.82-29.46 2.56-16.87 2.66-17.73 2.77-23.66 2.52a42.57 42.57 0 0 1-8-1.09c-17.7-4.16-46.18-5.86-54.72-3.01-2.72.9-5.88 2.8-9.52 5.59a112.37 112.37 0 0 0-6.54 5.48c-1.4 1.25-9.17 8.5-10.78 9.84-1.45 1.2-8.18 7.42-8.85 8.02a114.65 114.65 0 0 1-4.55 3.9c-4.99 4.03-8.9 6.2-11.92 6.2-3.52.05-4.32 0-5.14-.4-1.13-.56-1.5-1.72-1.13-3.57.74-3.63 4.47-10.84 12.84-24.8 5.69-9.48 9.42-18 11.78-26.2 1.45-5.04 1.94-7.4 2.97-14.54h1.01c-1.05 7.3-1.54 9.7-3.01 14.82-2.39 8.28-6.16 16.89-11.9 26.44-8.3 13.84-12 21.01-12.7 24.48-.3 1.45-.08 2.14.59 2.47.6.3 1.35.35 3.48.3 3.92 0 7.69-2.1 12.5-5.98 1.4-1.13 2.87-2.39 4.51-3.86.66-.59 7.41-6.83 8.88-8.05 1.59-1.33 9.34-8.55 10.75-9.82 2.4-2.15 4.55-3.96 6.6-5.53 3.72-2.85 6.97-4.8 9.81-5.74 8.76-2.92 37.41-1.22 55.27 2.99 2.57.6 5.14.95 7.81 1.06 5.84.25 6.7.14 23.47-2.51 11.05-1.75 19.36-2.57 29.6-2.57 5.2 0 15.99.3 15.05.29 2.87.03 3.84-.17 4.3-.83.23-.32.4-.8.58-1.7l.28-1.43.4-1.85c.88-3.6 2.36-7.44 4.96-12.22 1.87-3.43 3.44-7 4.73-10.76h1.06zm-8.59 0c-5.91 17.94-9.55 22-19.76 22-4.5 0-10.22.32-28.69 1.5l-1.53.1c-15.6.99-23.47 1.4-28.78 1.4-5.35 0-13.24-.96-28.86-3.28l-1.54-.23C163.18 18.75 157.47 18 153 18c-4.45 0-7.3 1.01-10.96 3.34-.1.06-1.8 1.17-2.3 1.47-2.43 1.5-4.32 2.19-6.74 2.19-2.8 0-4.11-1.46-4.11-4.22 0-1.04.16-2.29.5-4.1.16-.82.9-4.4 1.07-5.32.8-4.11 1.3-7.68 1.47-11.36h2c-.17 3.82-.68 7.5-1.5 11.75-.19.94-.92 4.5-1.07 5.31a21.04 21.04 0 0 0-.47 3.72c0 1.7.46 2.22 2.11 2.22 1.99 0 3.55-.57 5.7-1.9.47-.28 2.15-1.37 2.26-1.44C144.92 17.14 148.12 16 153 16c4.62 0 10.3.74 28.9 3.51l1.53.23C198.93 22.04 206.8 23 212 23c5.25 0 13.11-.41 28.65-1.4l1.54-.1C260.73 20.32 266.43 20 271 20c8.95 0 12.15-3.4 17.66-20h2.1zM141.51 0h1.13c-2.06 3.86-2.63 5.1-2.77 6.19-.15 1.12.42 1.64 2.32 1.96 1.8.3 3.85.35 10.81.35 6.02 0 13 .56 21.35 1.62 3.95.5 8.03 1.1 13.13 1.89 24 3.7 22.5 3.49 26.83 3.49 24.02 0 51.83-2.24 60.45-6.94 2.88-1.57 5.05-4.49 6.6-8.56h1.07c-1.64 4.47-3.98 7.69-7.2 9.44-8.83 4.82-36.67 7.06-60.92 7.06-4.41 0-2.84.22-26.98-3.5-5.1-.8-9.17-1.38-13.1-1.88-8.31-1.06-15.26-1.62-21.23-1.62-7.04 0-9.1-.05-10.97-.37-2.38-.4-3.38-1.32-3.15-3.07.16-1.22.69-2.41 2.63-6.06zm76.4 0c5.69 1.64 10.37 2.5 14.09 2.5 9.59 0 16.7-.71 22.4-2.5h2.98C251.12 2.53 243.2 3.5 232 3.5c-4.5 0-10.32-1.21-17.53-3.5h3.45zM70.69 0c-2.87 3.27-6.95 5.39-12.02 6.53-3.98.89-7.5 1.08-12.92 1A97.24 97.24 0 0 0 44 7.5c-5.37 0-8.86-1.24-10.1-4.97A8.6 8.6 0 0 1 33.5 0h.99c.02.82.14 1.56.36 2.22C35.91 5.39 39.02 6.5 44 6.5l1.76.02c5.35.09 8.8-.1 12.69-.97C62.95 4.54 66.63 2.74 69.3 0h1.37zM0 207.87c7.31-.16 11.5 3.33 11.5 11.13 0 11.41-5.05 28.35-11.5 41.5v-2.3c5.93-12.72 10.5-28.47 10.5-39.2 0-7.18-3.7-10.3-10.5-10.13v-1zm0 7.05c1.23.14 2.18.58 2.87 1.31 1.4 1.48 1.6 3.72 1.16 7.58l-.16 1.3A28.93 28.93 0 0 0 3.5 229c0 3.2-1.48 9.52-3.5 15.9v-3.45c1.49-5.13 2.5-9.87 2.5-12.45 0-.98.08-1.75.37-4.02l.16-1.29c.42-3.56.24-5.59-.88-6.77-.5-.53-1.21-.87-2.15-1v-1zM0 410.9v-1.47a21.67 21.67 0 0 0 2.97-4.7c1.32-2.7 2.68-6.28 4.56-11.89 7.85-23.55 7.83-26.6.25-30.4-2.25-1.12-4.8-1.43-7.78-.91v-1.02a13.1 13.1 0 0 1 8.22 1.04c8.24 4.12 8.26 7.6.25 31.6-1.88 5.66-3.25 9.27-4.6 12.02A20.82 20.82 0 0 1 0 410.9zM33.64 452c1.68 0 3.04-.23 8.34-1.31l2.38-.47c8.26-1.57 12.72-1.3 14.53 2.33 1.38 2.75-.47 5.86-4.75 9.68a75.6 75.6 0 0 1-5.08 4.07c-.94.7-4.89 3.59-5.79 4.27-1.86 1.4-2.97 2.37-3.47 3.03a19.08 19.08 0 0 0-2.89 5.5c.07-.2-4.02 13.65-6.96 22.22-2.7 7.85-5.56 10.72-8.82 8.59-2.11-1.4-3.66-4.24-6.6-11.03-1.98-4.62-2.5-5.76-3.4-7.4-4.55-8.18-3.9-23.9-.05-32.87a9.6 9.6 0 0 1 6.98-5.96c2.59-.66 4.86-.75 11.78-.67l3.8.02zm0 2c-1.13 0-2.09 0-3.82-.02-12.07-.13-14.83.57-16.9 5.41-3.63 8.47-4.26 23.55-.05 31.12.96 1.73 1.48 2.88 3.5 7.58 2.72 6.3 4.24 9.08 5.86 10.14 1.64 1.08 3.5-.8 5.82-7.55a682.9 682.9 0 0 0 6.97-22.24 21.03 21.03 0 0 1 3.18-6.04c.65-.87 1.85-1.9 3.86-3.43.92-.7 4.87-3.57 5.8-4.27 2.02-1.5 3.6-2.77 4.95-3.97 3.63-3.23 5.09-5.7 4.3-7.28-1.21-2.42-5.07-2.65-12.38-1.27l-2.35.47c-5.49 1.11-6.86 1.35-8.74 1.35zm345.63 146c-3.45-12.26-3.77-14.13-3.77-19 0-3.33-.13-6.27-.43-11.34-.63-10.33-.65-13.5.26-17.07 1.21-4.74 4.21-7.1 9.67-7.1h26c4.08 0 5.19 1.85 5.93 7.11.1.79.13.97.19 1.32.84 5.35 2.8 7.58 8.88 7.58 3.64 0 5.54.4 6.43 1.37.76.83.76 1.44.36 3.93-.85 5.26.5 8.85 7.5 13.8 6.32 4.45 11.63 5.36 16.55 3.37 3.8-1.54 6.73-4.16 11.92-10l1.1-1.23 1.09-1.23a75.6 75.6 0 0 1 2.7-2.86 35.81 35.81 0 0 1 9.57-6.73c1.52-.76 1.72-.86 5.66-2.63 6.1-2.73 9.01-4.5 11.74-7.62 2.63-3 4.67-4.85 6.7-6.04 3.18-1.85 5.46-2.13 13.68-2.13 5.98 0 10.56-4.32 18-14.99l2.82-4.03c1.06-1.5 1.94-2.7 2.79-3.79 7.87-10.12 19.38-10.4 30.74.96 5.54 5.53 10.17 19.43 13.64 38.51 2.5 13.75 4.18 29.46 4.47 39.84h-1c-.3-10.32-1.96-25.97-4.45-39.66-3.43-18.87-8.02-32.65-13.36-37.99-10.95-10.95-21.76-10.68-29.26-1.04-.83 1.07-1.7 2.26-2.75 3.75l-2.81 4.02c-7.65 10.95-12.38 15.42-18.83 15.42-8.04 0-10.21.26-13.17 2-1.92 1.12-3.9 2.9-6.45 5.83-2.86 3.26-5.87 5.09-12.09 7.88a103.35 103.35 0 0 0-5.62 2.6 34.84 34.84 0 0 0-9.32 6.54 74.67 74.67 0 0 0-3.75 4.05l-1.1 1.24c-5.28 5.95-8.29 8.64-12.28 10.25-5.26 2.13-10.92 1.17-17.5-3.48-7.33-5.17-8.82-9.15-7.92-14.77.34-2.12.34-2.6-.1-3.1-.64-.69-2.34-1.04-5.7-1.04-6.63 0-8.96-2.63-9.87-8.42l-.2-1.34c-.67-4.82-1.53-6.24-4.93-6.24h-26c-5 0-7.6 2.04-8.7 6.34-.88 3.43-.85 6.57-.23 16.76a177 177 0 0 1 .43 11.4c0 4.78.32 6.63 3.81 19h-1.04zm13.68 0c-1.31-6.58-1.61-10.71-1.36-14.84.04-.7.1-1.44.18-2.38l.23-2.56c.34-3.81.5-6.97.5-11.22 0-4.94 1.46-7.76 4.21-8.42 2.38-.58 5.56.54 9.2 3 6.64 4.52 13.99 13.07 16.55 19.23 4.77 11.44 14.12 15.69 33.54 15.69 8.6 0 14.32-2.35 20.67-7.88 1.45-1.26 15.06-15 21-20 7.21-6.07 11.77-7.59 20.62-8.32 5.52-.45 7.98-.9 11.44-2.36 4.58-1.95 9.36-5.48 14.9-11.29 7.43-7.76 13.25-8.92 17.47-4.3 3.32 3.63 5.46 10.58 6.82 20.24.73 5.17.94 7.74 1.58 17.38.25 3.75.17 5.32-.92 18.03h-1c1.09-12.7 1.17-14.28.92-17.97-.64-9.6-.85-12.16-1.57-17.3-1.33-9.47-3.43-16.27-6.56-19.7-3.76-4.11-8.93-3.08-16 4.32-5.65 5.9-10.54 9.5-15.25 11.5-3.58 1.53-6.13 1.99-11.6 2.44-8.8.72-13.17 2.18-20.2 8.1-5.9 4.96-19.5 18.7-21 19.99-6.52 5.68-12.47 8.12-21.32 8.12-19.78 0-29.5-4.42-34.46-16.3-2.49-5.97-9.71-14.38-16.2-18.79-3.42-2.32-6.36-3.35-8.4-2.86-2.2.53-3.44 2.92-3.44 7.45 0 4.28-.16 7.47-.5 11.31l-.23 2.56c-.09.93-.14 1.65-.19 2.35-.24 4.08.06 8.18 1.39 14.78h-1.02zm113.75 0c2.52-3.26 8.93-11.79 10.9-14.3 5.48-6.98 13.05-12.38 19.4-13.94 7.01-1.71 11.5 1.45 11.5 9.24 0 4.02-.04 5.16-.74 19h-1c.7-13.85.74-15 .74-19 0-7.12-3.86-9.83-10.26-8.26-6.11 1.5-13.5 6.77-18.85 13.57-1.86 2.36-7.65 10.07-10.43 13.69h-1.26zm-9.86-338.96c3.44 2.71 7 5.1 11.44 7.75 1.06.64 8.42 4.9 10.35 6.1 11.27 7 15 13.35 12.35 25.33-1.45 6.52-4.53 11.1-9.39 14.44-3.83 2.63-8.07 4.26-16.08 6.56-11.97 3.45-13.68 3.99-18.82 6.28a60.18 60.18 0 0 0-7.81 4.18c-11.11 7.07-19.1 7.7-27.96 3.28-3.56-1.77-17.2-11-17.2-11.01a101.77 101.77 0 0 0-5.2-3.07c-16.04-8.83-34.27-24.16-34.52-31.85-.11-3.46 1.99-6.57 6.28-10.26 1.03-.9 2.18-1.81 3.68-2.95.72-.55 3.38-2.56 3.94-3 4.47-3.4 7.18-5.79 9.32-8.45 11.12-13.82 26.55-28.68 34.36-32.28 12.06-5.54 19.84-5.77 27.37.12 3.25 2.54 5.65 6.54 8.58 13.35.29.65 2.3 5.45 2.88 6.74 1.62 3.65 2.9 5.8 4.24 6.94.72.6 1.45 1.2 2.2 1.8zm-3.49-.28c-1.63-1.39-3.03-3.74-4.77-7.65-.58-1.3-2.6-6.12-2.88-6.76-2.81-6.5-5.08-10.3-7.98-12.56-6.83-5.35-13.85-5.15-25.3.12-7.45 3.42-22.7 18.12-33.64 31.72-2.27 2.82-5.08 5.3-9.67 8.79l-3.94 2.98a79.98 79.98 0 0 0-3.59 2.88c-3.87 3.33-5.67 6-5.58 8.69.21 6.64 18.14 21.72 33.48 30.15 1.76.97 3.5 2 5.3 3.13.12.08 13.61 9.22 17.03 10.92 8.22 4.1 15.46 3.52 26-3.18a62.17 62.17 0 0 1 8.07-4.31c5.25-2.35 7-2.9 19.08-6.38 7.8-2.24 11.9-3.82 15.5-6.3 4.44-3.04 7.23-7.18 8.56-13.22 2.44-11.02-.83-16.6-11.45-23.2-1.9-1.18-9.23-5.42-10.32-6.08-4.5-2.69-8.13-5.12-11.64-7.9-.77-.6-1.52-1.21-2.26-1.84zM87.72 241.6c4.3-2.98 7.88-5 12.14-6.95.84-.4 1.73-.78 2.78-1.24l4.37-1.88a164.3 164.3 0 0 0 17.74-8.96 320.67 320.67 0 0 1 27.87-14.5c4.22-1.95 21.89-9.84 21.17-9.52 19.17-8.62 28.1-6.93 49.5 8.05 7.91 5.54 13.24 13.25 16.45 22.66 3.02 8.83 3.76 16.51 3.76 27.75 0 8.32-.66 12.95-3.68 18.97-4.18 8.36-12.3 16.14-25.58 23.47-24.45 13.49-38.83 27.55-52.83 47.84-8.83 12.8-47.76 44.21-65.16 54.15C75.04 413.55 48.89 423.5 31 423.5c-10.05 0-14.67-4.78-14.76-13.37-.07-6.32 2.06-13.73 6.3-24.32 2.95-7.37 2.02-12.9-2.16-22.29-3.19-7.17-3.88-9.14-3.88-12.52 0-3.35 1.87-6.9 5.52-11.07 2.61-3 3.5-3.83 11.9-11.5 5.09-4.66 8.08-7.6 10.7-10.75 9.46-11.36 12.62-19.47 17.9-44.78 3.12-15.05 6.63-20.28 15.12-25.25.8-.47 3.95-2.25 4.7-2.68a76.66 76.66 0 0 0 5.38-3.38zm.56.82a77.63 77.63 0 0 1-5.44 3.43l-4.7 2.67c-8.23 4.82-11.57 9.81-14.65 24.6-5.3 25.45-8.51 33.7-18.1 45.21-2.66 3.19-5.68 6.16-10.8 10.84-8.36 7.64-9.24 8.48-11.82 11.42-3.5 4.01-5.27 7.36-5.27 10.42 0 3.18.68 5.1 3.8 12.12 4.27 9.6 5.24 15.37 2.16 23.07-4.18 10.47-6.29 17.78-6.22 23.93.08 8.06 4.26 12.38 13.76 12.38 17.67 0 43.68-9.9 64.75-21.93 17.28-9.88 56.1-41.2 64.84-53.85 14.08-20.42 28.57-34.59 53.17-48.16 13.12-7.23 21.09-14.87 25.17-23.03 2.92-5.86 3.57-10.35 3.57-18.53 0-11.13-.74-18.73-3.7-27.43-3.15-9.22-8.36-16.75-16.09-22.16-21.13-14.8-29.7-16.42-48.5-7.95.7-.32-16.96 7.56-21.17 9.5-1.7.8-3.3 1.55-4.86 2.3a319.68 319.68 0 0 0-22.93 12.17 165.3 165.3 0 0 1-17.85 9.01l-4.37 1.88c-1.04.45-1.92.84-2.76 1.23a74.56 74.56 0 0 0-11.99 6.86zm-7.6 12.2c7.7-6.25 12.3-8.17 23.68-11.27 6.12-1.67 9.12-2.95 12.31-5.72 3.8-3.3 7.47-4.52 15.86-6.1 2.75-.52 3.67-.7 5.06-1.02 5.48-1.24 9.48-2.93 13.1-5.89 10.42-8.53 25.4-14.11 36.31-14.11 5.33 0 16.77 7.58 25.74 17.16 10.73 11.46 15.96 23.27 12.73 32.5-3.18 9.1-11.39 18.57-23.03 27.86-8.44 6.73-18.36 13-25.22 16.43-3.72 1.86-6.59 4.88-9.77 9.99-.69 1.1-11.1 20.25-16.03 27.83-5.62 8.65-15.4 17.36-30.23 27.96a552.58 552.58 0 0 1-9.2 6.42c-.13.09-6.81 4.65-8.6 5.89-6.47 4.46-10.35 7.35-13.05 9.83-11.64 10.67-37.14 15.54-43.7 8.98-1.96-1.96-2.2-4.06-1.95-10.52.37-9.42-.5-14.5-4.95-20.51a34.09 34.09 0 0 0-7.04-6.92c-3.93-2.95-6.07-6.11-6.56-9.49-.97-6.61 3.87-13.06 14.17-21.69 1.58-1.32 6.67-5.44 7.09-5.78a48.03 48.03 0 0 0 5.23-4.77c4.1-4.63 5.85-9.55 7.8-20.07a501.52 501.52 0 0 0 .8-4.37c.33-1.87.6-3.3.88-4.73.74-3.78 1.5-7.18 2.4-10.63 1-3.78 1.38-5.5 2.36-10.37.6-3.02.93-4.21 1.56-5.47 1.22-2.45 1.27-2.5 12.25-11.42zm.64.78c-10.77 8.74-10.88 8.84-12 11.08-.58 1.16-.88 2.3-1.47 5.22-.98 4.89-1.36 6.63-2.37 10.44-.9 3.43-1.65 6.8-2.39 10.56a339.79 339.79 0 0 0-1.29 6.95l-.39 2.15c-1.98 10.68-3.77 15.74-8.04 20.54a48.77 48.77 0 0 1-5.34 4.88c-.42.34-5.5 4.47-7.07 5.78-10.04 8.4-14.72 14.65-13.83 20.78.45 3.1 2.44 6.03 6.17 8.83 3 2.25 5.39 4.62 7.24 7.12 4.63 6.24 5.52 11.52 5.15 21.15-.25 6.14-.01 8.1 1.66 9.78 6.1 6.1 31.02 1.33 42.31-9.02 2.75-2.52 6.66-5.43 13.16-9.92l8.6-5.89c3.63-2.48 6.45-4.44 9.19-6.4 14.73-10.54 24.44-19.18 29.97-27.7 4.9-7.54 15.31-26.68 16.02-27.8 3.27-5.26 6.26-8.41 10.18-10.37 6.79-3.4 16.65-9.63 25.03-16.32 11.52-9.18 19.61-18.53 22.72-27.4 3.07-8.78-2.02-20.27-12.52-31.49-8.8-9.4-20.04-16.84-25.01-16.84-10.67 0-25.43 5.5-35.68 13.89-3.76 3.07-7.9 4.81-13.5 6.09-1.41.32-2.35.5-5.11 1.02-8.21 1.55-11.76 2.73-15.38 5.88-3.34 2.9-6.45 4.22-12.7 5.92-11.26 3.07-15.75 4.94-23.31 11.09zM212 251.85c0 7.56-.6 10.92-2.6 14.3-1.1 1.84-7.66 10.05-8.6 11.3-5.96 7.94-9.33 10.28-17.26 13.76-1.34.58-2.2 1-3.03 1.5-.55.33-1.2.66-2 1.02-.71.33-4.46 1.9-5.52 2.39-6.05 2.78-8.99 5.8-8.99 10.73 0 10.97-18.95 36.12-34.51 44.87-8.18 4.6-21.3 9.36-32.78 11.86-13.33 2.9-22.49 2.48-24.62-2.32-1.32-2.97-4.4-4.26-11.98-5.81l-.6-.12c-4.84-.99-6.94-1.55-9.03-2.64-2.92-1.5-4.48-3.7-4.48-6.84 0-2.74 1.08-5.77 3.25-9.67.85-1.53 1.82-3.13 3.23-5.35-.16.25 2.83-4.4 3.67-5.76 6.69-10.7 9.85-18.5 9.85-27.22 0-18.41 11.22-33.37 27.5-42.86 5.22-3.05 9.23-3.31 15.2-2.12 5.04 1 6.05.9 7.43-1.52 4.5-7.85 7.04-9.5 15.87-9.5 3.93 0 6.97-.98 10.47-3.16 1.56-.97 8.67-6.17 10.99-7.68 9.2-5.98 11.34-7 25.2-11.95 6.95-2.48 15.18 1.28 22.33 9.12 6.55 7.19 11.01 16.61 11.01 23.67zm-2 0c0-6.5-4.25-15.48-10.49-22.32-6.67-7.32-14.16-10.74-20.17-8.59-13.73 4.9-15.73 5.85-24.8 11.75-2.24 1.46-9.37 6.68-11.01 7.7-3.8 2.36-7.2 3.46-11.53 3.46-8.08 0-9.98 1.23-14.13 8.5-1.1 1.91-2.51 2.88-4.35 3.09-1.3.14-1.9.05-5.22-.61-5.53-1.1-9.07-.88-13.8 1.88-15.72 9.17-26.5 23.55-26.5 41.14 0 9.2-3.28 17.29-10.15 28.28l-3.68 5.77c-1.39 2.19-2.35 3.77-3.17 5.25-2.02 3.63-3 6.38-3 8.7 0 4.19 2.87 5.67 11.9 7.52l.61.12c8.27 1.7 11.7 3.13 13.4 6.95 3.17 7.14 36 0 54.6-10.46 14.98-8.43 33.49-32.99 33.49-43.13 0-5.9 3.47-9.48 10.16-12.55 1.1-.5 4.85-2.08 5.52-2.38.74-.34 1.32-.64 1.8-.93.92-.55 1.85-1 3.25-1.62 7.65-3.35 10.75-5.5 16.47-13.12 1.02-1.36 7.47-9.42 8.47-11.11 1.79-3.01 2.33-6.06 2.33-13.3zm-37.18-22.4c.15-.1 2.4-1.51 2.95-1.84.96-.57 1.7-.94 2.43-1.17 2.57-.83 5.06-.1 11.04 3.12 14.86 8 19.43 22.87 9.18 38.71-4.04 6.24-9.37 9-18.72 11.11-.85.2-1.2.27-3.13.68-6.04 1.29-8.78 2.08-11.6 3.65-3.63 2.02-6.09 4.98-7.5 9.44-7.87 24.93-19.72 43.34-36.28 50.31-16.45 6.93-21.13 8.53-27.98 8.89-4.94.25-9.8-.65-15.4-2.89a44.45 44.45 0 0 1-5.64-2.6c-4.02-2.33-5.14-4.74-4.5-9.31.3-2.13 3.77-15.53 4.84-20.65.63-3.05 1.19-6.14 1.75-9.69a464.04 464.04 0 0 0 1.35-8.9c1.42-9.41 2.5-14.27 4.49-18.65 2.46-5.43 6.13-9.03 11.72-11.13 6.59-2.47 10.54-3.1 18.03-3.53 4.75-.27 6.68-.64 9-2.05.61-.37 1.22-.81 1.82-1.33a30.61 30.61 0 0 0 3.37-3.4c.59-.69 2.38-2.9 2.63-3.19 3.36-4 6.3-5.53 12.33-5.53 3.94 0 5.9-.92 8.18-3.36-.17.18 2.75-3.14 3.85-4.22a30.95 30.95 0 0 1 6.79-5c1.5-.83 3.15-1.62 4.99-2.38a64.92 64.92 0 0 0 10.01-5.1zm-14.52 8.34a29.95 29.95 0 0 0-6.57 4.84 116.68 116.68 0 0 0-3.82 4.2c-2.46 2.63-4.68 3.67-8.91 3.67-5.72 0-8.39 1.39-11.57 5.17-.23.28-2.03 2.5-2.63 3.2a31.6 31.6 0 0 1-3.47 3.51c-.65.55-1.3 1.03-1.96 1.43-2.5 1.51-4.55 1.9-9.47 2.19-7.39.42-11.25 1.04-17.72 3.47-5.34 2-8.82 5.4-11.17 10.6-1.93 4.27-3 9.07-4.41 18.39l-.65 4.34-.7 4.57c-.57 3.56-1.12 6.67-1.76 9.73-1.08 5.18-4.54 18.53-4.83 20.59-.59 4.17.35 6.18 4.01 8.3 1.35.77 3.1 1.58 5.52 2.55 5.46 2.18 10.18 3.05 14.97 2.8 6.69-.34 11.32-1.93 27.65-8.8 16.21-6.83 27.92-25.01 35.71-49.7 1.49-4.7 4.12-7.86 7.97-10 2.93-1.63 5.74-2.45 11.87-3.76 1.92-.4 2.28-.49 3.12-.68 9.12-2.06 14.24-4.7 18.1-10.67 9.92-15.34 5.55-29.55-8.82-37.29-5.75-3.1-8.03-3.76-10.25-3.05-.65.2-1.33.54-2.23 1.08-.55.32-2.77 1.72-2.93 1.82a65.91 65.91 0 0 1-10.16 5.17c-1.8.75-3.42 1.52-4.89 2.33zm-42.39 32.72c16.15-2.87 26.36-.97 32.47 6.16 5.08 5.93 1.13 21.42-5.93 35.55-4.79 9.58-10.6 16.21-23.16 25.19-14.15 10.1-35.5 12.2-40.71 3.85-1.86-2.97-2.1-8.14-1.06-15.73.78-5.68 1.86-10.71 4.73-22.98l.12-.51c1.59-6.8 2.37-10.31 3.14-14.14 1.45-7.25 3.74-11.47 7.26-13.74 2.81-1.8 5.53-2.28 12.33-2.62 5.33-.27 7.56-.46 10.81-1.03zm.18.98c-3.3.59-5.56.78-10.94 1.05-6.62.33-9.23.78-11.84 2.46-3.25 2.1-5.42 6.09-6.82 13.1-.77 3.84-1.56 7.35-3.15 14.17l-.12.5c-2.86 12.24-3.93 17.26-4.7 22.9-1.03 7.36-.79 12.36.9 15.07 4.82 7.7 25.54 5.67 39.29-4.15 12.43-8.88 18.13-15.39 22.84-24.81 6.86-13.72 10.75-29 6.07-34.45-5.84-6.81-15.7-8.65-31.53-5.84zM132 276.5c7.12 0 10.66 3.08 11.25 8.7.42 4.02-.43 8.14-2.77 15.94-2.56 8.52-18.36 25.38-27.2 31.28-7.01 4.67-20.02 5.67-26.57.99-3.99-2.85-3.53-12.08.02-26.46.68-2.75 1.47-5.65 2.37-8.76a412.6 412.6 0 0 1 3.05-10.14l.37-1.2c1.48-4.8 5.1-7.75 10.73-9.27 4.4-1.2 9.54-1.5 17.48-1.33l3.89.1c3.87.11 5.42.15 7.38.15zm0 1c-1.97 0-3.53-.04-7.41-.15l-3.88-.1c-7.85-.17-12.92.13-17.2 1.3-5.32 1.43-8.67 4.16-10.03 8.6a1277.83 1277.83 0 0 1-1.6 5.21c-.68 2.2-1.27 4.17-1.82 6.1-.9 3.1-1.68 5.99-2.36 8.73-3.43 13.88-3.87 22.93-.4 25.4 6.17 4.42 18.73 3.45 25.42-1 8.66-5.78 24.33-22.49 26.8-30.73 2.3-7.67 3.14-11.71 2.73-15.56-.53-5.1-3.64-7.8-10.25-7.8zm-17.79 7a31.3 31.3 0 0 1 8.57 1.4c5.42 1.78 8.72 5.03 8.72 10.1 0 9.59-9.51 17.2-22.34 21.47-9.82 3.28-13.62-1.79-11.66-16.54.84-6.28 3.82-10.67 8.24-13.46a20.38 20.38 0 0 1 8.47-2.97zm-.6 1.08a19.39 19.39 0 0 0-7.34 2.73c-4.18 2.64-6.98 6.78-7.77 12.76-1.89 14.11 1.36 18.45 10.34 15.46C121.3 312.37 130.5 305 130.5 296c0-4.56-2.98-7.5-8.03-9.15a28.05 28.05 0 0 0-8.2-1.35c-.13 0-.35.03-.66.08zm80.87-23.45c-2.72 9.8-14.93 9.86-26.72 3.3-10.17-5.64-13.8-17.98-5-22.87a66.53 66.53 0 0 0 4.48-2.7l2.03-1.3a50.15 50.15 0 0 1 3.92-2.3c4.73-2.43 8.82-2.8 14-.72 9.16 3.66 10.98 13.33 7.3 26.6zm-20.83-24.98a49.26 49.26 0 0 0-3.84 2.25l-2.03 1.3c-.84.53-1.5.95-2.16 1.35-.82.5-1.6.96-2.38 1.39-7.94 4.4-4.59 15.8 5 21.12 11.31 6.29 22.8 6.23 25.28-2.7 3.57-12.83 1.85-21.97-6.7-25.4-4.9-1.95-8.69-1.62-13.17.7zm17.85 12.15c0 5.7-2.44 9-6.64 9.96-3.3.76-7.56-.05-11.08-1.81l-1.89-.94c-.67-.34-1.18-.62-1.63-.88-4.07-2.38-4.13-4.97.34-10.93 6.8-9.06 20.9-7.16 20.9 4.6zm-1 0c0-5.3-2.87-8.55-7.32-9.16-4.23-.57-8.99 1.44-11.78 5.16-4.15 5.54-4.1 7.44-.64 9.47.44.25.93.51 1.59.85l1.87.93c3.34 1.67 7.36 2.44 10.42 1.74 3.73-.86 5.86-3.74 5.86-9zM387 530.3c0-12.8 2.44-16.74 18.48-29.77a56.8 56.8 0 0 1 7.61-5.2c2.6-1.5 5.33-2.82 8.5-4.18 1.24-.53 2.48-1.05 4.1-1.7l3.92-1.57c9.4-3.83 13.74-6.7 16.62-12.05 1.2-2.22 2.21-4.4 3.23-6.83a148.57 148.57 0 0 0 1.54-3.84l.3-.74.56-1.44c3.2-8.02 6.05-12.08 12.7-16.5a35.26 35.26 0 0 0 4.96-4 46.36 46.36 0 0 0 3.88-4.29c.27-.34 2.55-3.2 3.2-3.98 3.48-4.15 6.51-5.9 11.51-5.9 3.08 0 5.62-.63 9.57-2.1 5.42-2.02 6.53-2.34 8.96-2.2 2.53.13 4.85 1.26 7.18 3.59 1.3 1.3 5.55 5.83 6.52 6.78 5.06 5 9.44 6.92 17.77 6.92a197.5 197.5 0 0 1 12.08.45c15.93.87 21.94.57 25.28-2.21 6.91-5.77 11.64-2.73 11.64 7.76 0 10.73-8.6 20-19 20-4.8 0-8.32 1.43-9.34 3.67-1.12 2.48.68 6.15 5.98 10.57 13.6 11.33 11.24 20.76-7.64 20.76a21.91 21.91 0 0 0-14.6 5.24c-3.28 2.71-5.8 5.86-9.85 11.82l-1.52 2.25c-3.1 4.57-5.01 7.1-7.32 9.4-6.21 6.21-9.3 7.64-13.05 6.89l-1-.23a10.82 10.82 0 0 0-2.66-.37c-1.6 0-2.41.67-8.18 6.22-4.85 4.67-8.07 6.78-11.82 6.78-1.33 0-3.46 1.15-6.45 3.45-1.27.98-2.68 2.14-4.5 3.7l-4.92 4.29a181.11 181.11 0 0 1-4.54 3.82c-9.33 7.56-15.63 10.2-20.21 6.52-2.7-2.15-4.14-4.51-4.63-7.26-.37-2.04-.26-3.63.29-7.3.87-5.85.65-8.42-1.83-11.6-2.32-2.98-2.96-3.22-3.77-2.39-.25.26-1.35 1.63-1.61 1.94-2.21 2.5-4.85 3.57-9 2.82-4.6-.84-5.57-4.11-4.72-10.09l.24-1.56c.6-3.66.68-4.93.25-5.8-.44-.86-1.9-.94-5.23.4l-.74.29c-13.78 5.54-15.26 6.09-19.43 6.67-6.03.84-9.31-1.6-9.31-7.9zm2 0c0 5 2.14 6.6 7.04 5.92 3.91-.55 5.43-1.1 18.95-6.55l.75-.3c4.17-1.66 6.7-1.54 7.76.58.71 1.43.62 2.76-.06 7l-.24 1.53c-.72 5.04-.06 7.27 3.09 7.84 3.43.62 5.38-.17 7.15-2.18.2-.23 1.34-1.66 1.68-2 1.9-1.96 3.82-1.25 6.78 2.55 2.9 3.74 3.17 6.77 2.22 13.12-1 6.75-.52 9.4 3.62 12.71 3.49 2.8 9.1.45 17.7-6.51 1.35-1.1 2.75-2.28 4.49-3.78l4.93-4.3c1.84-1.58 3.27-2.76 4.58-3.77 3.34-2.56 5.74-3.86 7.67-3.86 3.04 0 5.95-1.9 10.43-6.22l2.46-2.39c.94-.89 1.67-1.56 2.37-2.13 1.81-1.49 3.3-2.26 4.74-2.26 1.03 0 1.81.13 3.1.42.7.16.71.17.96.21 2.96.6 5.45-.55 11.23-6.33 2.2-2.2 4.06-4.65 7.09-9.11l1.52-2.25c4.15-6.11 6.76-9.37 10.22-12.24a23.9 23.9 0 0 1 15.88-5.7c16.87 0 18.62-7.01 6.36-17.23-5.9-4.92-8.12-9.41-6.52-12.93 1.42-3.12 5.67-4.84 11.16-4.84 9.25 0 17-8.34 17-18 0-8.94-2.88-10.79-8.36-6.23-3.94 3.28-9.98 3.59-26.67 2.68l-1.02-.06c-5.09-.27-7.99-.39-10.95-.39-8.88 0-13.76-2.14-19.18-7.5-1-.98-5.26-5.53-6.53-6.79-1.99-1.99-3.86-2.9-5.87-3-2.03-.12-3.06.18-8.15 2.07-4.15 1.55-6.9 2.22-10.27 2.22-4.33 0-6.84 1.46-9.98 5.2-.63.74-2.89 3.6-3.18 3.95a48.29 48.29 0 0 1-4.04 4.46 37.26 37.26 0 0 1-5.24 4.23c-6.26 4.17-8.9 7.91-11.95 15.58l-.57 1.43-.28.74a531.5 531.5 0 0 1-1.56 3.88 77.49 77.49 0 0 1-3.32 7c-3.16 5.88-7.82 8.97-17.63 12.96l-3.92 1.58c-1.6.64-2.84 1.15-4.05 1.67a79.2 79.2 0 0 0-8.3 4.08 54.8 54.8 0 0 0-7.35 5.02C391.12 514.78 389 518.21 389 530.31zm133.22-79.76c3.06 1.53 6.54 2.02 10.68 1.7 2.53-.2 4.91-.62 8.8-1.49 5.36-1.19 6.33-1.38 8.33-1.54 2.78-.23 4.82.17 6.29 1.4 1.58 1.31 1.96 2.72 1.26 4.22-.66 1.38-1.05 1.74-5.05 5.07-3.53 2.93-5.03 4.83-5.03 7.09 0 7.3 1.29 10.02 7.83 15.62 3.86 3.3 5.93 6.84 5.28 9.62-.75 3.25-4.96 5.02-12.61 5.02-7.18 0-12.7 4.61-20.03 14.68-.5.7-3.96 5.57-4.94 6.87a38.89 38.89 0 0 1-4.72 5.5c-1.06.98-2.09 1.7-3.1 2.15-2.85 1.26-5.05 1.57-9.83 1.74-7.66.27-10.87 1.45-14.98 7.1-1.58 2.17-3.11 4-4.68 5.6a42.87 42.87 0 0 1-8.65 6.69c-.15.08-10.69 6.19-14.8 8.83-3.76 2.42-6.45 2.04-8.22-.77-1.28-2.03-1.9-4.54-2.87-10.35-.84-5.08-1.27-7.08-2.06-8.93-.97-2.3-2.21-3.24-4.02-2.88-6.2 1.24-8.95 1.39-10.98.2-2.37-1.4-3.13-4.62-2.62-10.73.16-1.96-1.04-2.87-3.76-3.04-2.24-.13-4.9.2-9.94 1.12l-.69.12c-7.97 1.45-10.72 1.72-12.72.73-2.91-1.43-1.6-5.27 4.23-12.21 5.48-6.53 10.6-10.81 15.76-13.53 3.74-1.97 5.94-2.65 12.16-4.1 7.29-1.72 10.4-3.51 14.04-9.31 2.96-4.75 10.74-18.62 12.14-20.84 3.59-5.67 6.8-9.1 11.05-11.34 2.6-1.38 4.72-2.82 9.17-6.07l1.38-1.01c7.85-5.72 12.3-7.98 17.68-7.98 4.22 0 6.49 1.36 9.13 4.77.34.43 1.67 2.22 2 2.67.85 1.09 1.6 1.98 2.45 2.83a24.29 24.29 0 0 0 6.64 4.78zm-.44.9c-2.8-1.4-5-3.03-6.92-4.97-.87-.9-1.65-1.81-2.51-2.93-.35-.46-1.68-2.25-2.01-2.67-2.47-3.18-4.46-4.38-8.34-4.38-5.09 0-9.4 2.2-17.09 7.78l-1.38 1.01c-4.49 3.29-6.63 4.74-9.3 6.15-4.06 2.15-7.16 5.45-10.66 11-1.39 2.19-9.16 16.05-12.15 20.82-3.79 6.07-7.13 7.98-14.66 9.75-6.13 1.45-8.27 2.1-11.92 4.02-5.04 2.66-10.05 6.86-15.46 13.3-5.43 6.46-6.53 9.69-4.55 10.66 1.7.84 4.48.57 12.1-.81l.7-.13c5.12-.93 7.82-1.27 10.17-1.12 3.21.2 4.92 1.48 4.7 4.11-.48 5.76.2 8.64 2.13 9.78 1.73 1.02 4.34.88 10.27-.31 2.35-.47 4 .78 5.14 3.47.83 1.95 1.27 4 2.07 8.8l.06.36c.94 5.65 1.55 8.11 2.72 9.98 1.46 2.3 3.52 2.6 6.84.46 4.14-2.66 14.69-8.77 14.81-8.85a41.9 41.9 0 0 0 8.46-6.54 47.89 47.89 0 0 0 4.6-5.48c4.32-5.95 7.81-7.23 15.74-7.5 4.66-.17 6.76-.47 9.46-1.67.9-.4 1.85-1.06 2.84-1.96a38.03 38.03 0 0 0 4.6-5.36c.96-1.3 4.4-6.16 4.93-6.87 7.5-10.31 13.22-15.09 20.83-15.09 7.24 0 11.02-1.6 11.64-4.24.54-2.32-1.36-5.55-4.97-8.64-6.75-5.79-8.17-8.79-8.17-16.38 0-2.67 1.64-4.74 5.39-7.86 3.8-3.17 4.23-3.56 4.78-4.73.5-1.06.25-1.99-.99-3.03-2.23-1.85-4.72-1.65-13.76.36-3.93.87-6.35 1.3-8.94 1.5-4.3.34-7.97-.18-11.2-1.8zm-28-3.9c5.65-2.82 8.96-2.2 12.9 1.37.56.5 2.6 2.47 3.02 2.87 4.2 3.89 8.07 5.71 14.3 5.71 11.37 0 14 1.41 16.1 8.09.26.83 1.35 4.6 1.66 5.62.8 2.63 1.64 5.03 2.7 7.6 2.13 5.17 2.64 8.32 1.72 10.24-.77 1.61-2.1 2.18-5.37 2.79-2.32.43-2.8.53-3.85.85-1.85.58-3.35 1.4-4.6 2.66-1 1-2.02 2.13-3.31 3.66-.6.71-2.91 3.5-3.46 4.14-7.2 8.54-12.43 12.35-19.59 12.35-3.76 0-6.95 1.28-10.59 4-1.84 1.37-11.62 10.31-15.22 13.06a73.09 73.09 0 0 1-8.95 5.88c-4.58 2.54-7.35 3.22-8.98 2.23-1.32-.8-1.65-2.07-1.94-5.5a52.53 52.53 0 0 0-.16-1.81c-.54-4.73-2.24-6.86-7.16-6.86-7.11 0-8.85-1.23-9.73-5.41-.96-4.61-2.1-6.7-6.55-9.67-3.97-2.65-4.31-5.42-1.52-8.22 2-2 4.63-3.5 11.35-6.87 6.61-3.3 9.2-4.8 11.1-6.68a39.09 39.09 0 0 0 5.3-6.48c.98-1.5 1.83-3.04 2.88-5.13l2.12-4.3c.91-1.83 1.72-3.37 2.61-4.98 5.74-10.32 10.37-14.78 23.22-21.2zm-22.34 21.7c-.89 1.59-1.69 3.12-2.6 4.94l-2.11 4.3a52.9 52.9 0 0 1-2.94 5.23 40.08 40.08 0 0 1-5.44 6.63c-2 2-4.62 3.51-11.35 6.87-6.6 3.3-9.2 4.8-11.1 6.69-2.33 2.34-2.08 4.37 1.38 6.67 4.7 3.14 5.96 5.46 6.97 10.3.78 3.7 2.09 4.62 8.75 4.62 5.5 0 7.57 2.57 8.15 7.75.06.5.09.82.17 1.84.25 3.06.55 4.17 1.46 4.72 1.2.74 3.69.13 7.98-2.25a72.09 72.09 0 0 0 8.82-5.8c3.55-2.7 13.34-11.65 15.24-13.07 3.79-2.83 7.18-4.19 11.18-4.19 6.77 0 11.8-3.67 18.83-12l3.45-4.13a60.07 60.07 0 0 1 3.37-3.72 11.72 11.72 0 0 1 5.01-2.91c1.1-.34 1.6-.45 3.97-.89 2.95-.55 4.07-1.02 4.65-2.23.76-1.59.28-4.5-1.74-9.43a84.46 84.46 0 0 1-2.74-7.69c-.31-1.03-1.4-4.8-1.66-5.61-1.95-6.2-4.16-7.39-15.14-7.39-6.5 0-10.61-1.93-14.98-5.98-.44-.4-2.46-2.37-3.01-2.86-3.65-3.3-6.52-3.85-11.79-1.21-12.67 6.33-17.15 10.65-22.78 20.8zm55.86 11.93c-2.98 6.45-16.78 15.26-26.74 15.26-5.33 0-7.56-2.98-7.11-7.86.32-3.48 2.1-7.91 3.93-10.61l1.52-2.32a44.95 44.95 0 0 1 1.88-2.7c3.66-4.8 7.85-7.45 13.62-7.45 9.06 0 15.75 9.52 12.9 15.68zm-.9-.42c2.52-5.47-3.65-14.26-12-14.26-5.4 0-9.33 2.48-12.82 7.06-.6.8-1.17 1.6-1.85 2.64 0 0-1.2 1.87-1.52 2.33-1.74 2.57-3.46 6.85-3.77 10.14-.4 4.33 1.43 6.77 6.12 6.77 9.57 0 23.02-8.58 25.83-14.68zm-69.67 20.74c2.08.18 4.44.81 5.88 1.8 2.12 1.47 2.2 3.6-.26 6.05-5.14 5.15-12.85 4.34-12.85-1.35 0-4.66 3.14-6.84 7.23-6.5zm-.09 1c-3.56-.3-6.14 1.5-6.14 5.5 0 4.58 6.53 5.26 11.15.65 2.03-2.04 1.98-3.43.4-4.52-1.27-.88-3.48-1.47-5.4-1.63zm29.59-225.95c4.64 2.35 17.27 8.24 19.39 9.43a24.14 24.14 0 0 1 7.05 5.64 45.03 45.03 0 0 1 3.75 5.2c2.4 3.78.04 7.66-6.2 11.63-4.97 3.16-12.18 6.3-21.95 9.82-4.84 1.74-19.63 6.68-21.1 7.2-6.59 2.33-14.85.1-25.14-5.86-3.93-2.27-8-5-12.94-8.54-2.23-1.61-9.5-6.99-10.7-7.85a81.21 81.21 0 0 0-8.63-5.7c-4.82-2.6-4.45-6.64.17-12.13 3.27-3.88 4.17-4.67 18.1-16.33a230.2 230.2 0 0 0 8.89-7.74 95.2 95.2 0 0 0 4.72-4.66c5.08-5.43 9.8-6.49 14.97-3.92 2.24 1.1 4.53 2.85 7.43 5.52 1.48 1.37 6.94 6.72 7.98 7.7 5.2 4.91 9.46 8.2 14.2 10.6zm-.46.9c-4.85-2.45-9.18-5.79-14.44-10.76-1.05-1-6.5-6.34-7.97-7.69-2.83-2.61-5.06-4.3-7.2-5.37-4.75-2.36-9-1.4-13.8 3.71a96.18 96.18 0 0 1-4.76 4.71c-2.48 2.3-5.16 4.62-8.92 7.77-13.86 11.6-14.77 12.4-17.98 16.21-4.28 5.08-4.58 8.4-.46 10.61 2.23 1.2 4.9 2.99 8.74 5.77 1.2.87 8.47 6.24 10.7 7.85a154.8 154.8 0 0 0 12.85 8.49c10.06 5.82 18.07 7.98 24.3 5.78 1.48-.52 16.27-5.47 21.1-7.2 9.7-3.5 16.86-6.61 21.75-9.72 5.84-3.71 7.9-7.1 5.9-10.26a44.09 44.09 0 0 0-3.67-5.08 23.16 23.16 0 0 0-6.78-5.42c-2.08-1.16-14.68-7.05-19.36-9.4zm-38.83 8.05c3.11-.37 5.7-.13 8.4.7 2.15.66 2.74.93 8.64 3.77 4.75 2.29 8.39 3.86 13.19 5.56 8.38 2.97 11.32 6.23 8.83 9.76-2.08 2.94-8.04 5.92-17.84 9.18-8.45 2.82-15.48 2.35-21.43-.9-4.65-2.55-8.33-6.5-12.15-12.3-2.9-4.41-2.73-8.2.16-11.06 2.48-2.45 6.87-4.07 12.2-4.7zm.12 1c-5.13.6-9.33 2.16-11.62 4.42-2.53 2.5-2.68 5.77-.02 9.8 3.73 5.68 7.3 9.51 11.8 11.97 5.7 3.11 12.43 3.57 20.62.84 9.59-3.2 15.44-6.12 17.34-8.82 1.94-2.75-.5-5.45-8.35-8.24-4.84-1.72-8.5-3.3-13.28-5.6-5.84-2.81-6.42-3.07-8.5-3.71a18.42 18.42 0 0 0-8-.66zM202.5 500.38c0 4.78-1.45 7.56-4.43 8.93-2.29 1.05-4.55 1.23-10.79 1.2l-1.78-.01c-9.19 0-17-7.65-17-15.5 0-7.59 10.6-10.51 19.74-5.44 2.78 1.55 4.21 1.94 8.57 2.75 4.44.83 5.69 2.27 5.69 8.07zm-1 0c0-5.3-.9-6.34-4.88-7.08-4.45-.83-5.96-1.25-8.86-2.86-8.57-4.76-18.26-2.1-18.26 4.56 0 7.3 7.36 14.5 16 14.5h1.79c6.06.04 8.26-.14 10.36-1.1 2.6-1.2 3.85-3.6 3.85-8.02zm33.33-117.85c3.71-1.31 8.7-2.7 16.1-4.55 2.58-.65 16.53-4.04 20.56-5.05 19.59-4.93 31.55-8.9 38.23-13.35 14.93-9.95 36.87-33.88 43.83-47.8 2.25-4.5 4.65-6.38 7.68-6.25 1.26.06 2.61.45 4.32 1.2a50.81 50.81 0 0 1 3.54 1.7l1.26.63c4.78 2.34 8.38 3.44 12.65 3.44 7.2 0 10.01 3.07 8.35 7.91-1.4 4.06-5.92 8.91-11.1 12.02-8.3 4.98-11.75 17.3-11.75 33.57 0 3.59-1.37 6.28-3.98 8.36-1.98 1.58-4.2 2.6-8.47 4.16l-1.02.37c-4.85 1.75-6.98 2.77-8.68 4.46-5.09 5.1-12.54 7.15-20.35 7.15-1.38 0-2.47.92-3.99 3.1-.29.41-1.32 1.95-1.47 2.18-2.68 3.92-4.93 5.72-8.54 5.72-7.84 0-10.74.93-21.76 6.94-5.18 2.82-8.8 3.58-14.66 3.68-.26 0-.47 0-.92.02-4.82.06-7.12.3-10.51 1.34a73.43 73.43 0 0 0-8.89 3.56c-2.17 1-10.53 5.01-10.23 4.87-7.79 3.7-13.32 5.98-18.9 7.57-12.41 3.55-18.58 2.24-27.42-4.07-2.58-1.85-2.72-4.43-.83-7.62 1.45-2.45 3.9-5.09 8.08-8.97l1.78-1.64c3.92-3.6 4.48-4.11 5.9-5.53 2.32-2.32 3.12-3.5 5.48-7.63 1.93-3.36 3.37-5.11 6.27-7.06 2.3-1.54 5.34-2.98 9.44-4.43zm.34.94c-4.03 1.42-7 2.83-9.22 4.32-2.75 1.85-4.1 3.49-5.96 6.73-2.4 4.2-3.24 5.44-5.64 7.83-1.43 1.44-2 1.96-5.94 5.57l-1.77 1.63c-4.1 3.82-6.52 6.41-7.9 8.75-1.65 2.79-1.54 4.8.55 6.3 8.6 6.14 14.46 7.38 26.57 3.92 5.5-1.57 11-3.84 18.74-7.51-.3.14 8.06-3.88 10.24-4.88a74.3 74.3 0 0 1 9.01-3.6c3.51-1.09 5.89-1.33 10.8-1.4h.91c5.72-.1 9.18-.83 14.2-3.57 11.16-6.08 14.2-7.06 22.24-7.06 3.19 0 5.2-1.6 7.71-5.28l1.48-2.2c1.7-2.43 3-3.52 4.81-3.52 7.57 0 14.78-2 19.65-6.85 1.83-1.84 4.04-2.9 9.04-4.7l1.02-.37c8.6-3.13 11.79-5.67 11.79-11.58 0-16.6 3.53-29.2 12.24-34.43 5-3 9.35-7.67 10.66-11.48 1.42-4.13-.83-6.59-7.4-6.59-4.45 0-8.19-1.14-13.09-3.54-7.52-3.67-6.78-3.34-8.72-3.43-2.58-.1-4.65 1.52-6.74 5.7-7.04 14.07-29.1 38.14-44.17 48.19-6.81 4.54-18.84 8.52-38.55 13.48-4.03 1.02-17.98 4.4-20.56 5.05-7.37 1.84-12.33 3.23-16 4.52zM252 387.5c2.08 0 4-.2 7.25-.69 5.22-.77 6.64-.9 8.46-.5 2.52.56 3.79 2.35 3.79 5.69 0 4.05-2.27 7.29-6.62 10.11-3.24 2.1-6.53 3.53-14.15 6.4l-.27.1-2.28.86c-3.04 1.16-5.27 2.52-9.33 5.43l-.8.57c-8.19 5.88-13.35 8.03-23.05 8.03-4.98 0-6.88-2.03-5.75-5.62.87-2.81 3.58-6.56 7.8-11.13 1.26-1.37 2.64-2.8 4.15-4.3 3.17-3.14 11.25-10.61 11.45-10.8.46-.47.93-.89 1.4-1.26 3.38-2.71 5.77-3.08 14.18-2.93 1.65.03 2.63.04 3.77.04zm0 1c-1.15 0-2.13-.01-3.79-.04-8.18-.14-10.4.2-13.54 2.71-.44.35-.88.74-1.32 1.18-.2.21-8.3 7.69-11.45 10.82a134.6 134.6 0 0 0-4.12 4.26c-4.12 4.47-6.76 8.12-7.58 10.75-.9 2.88.45 4.32 4.8 4.32 9.46 0 14.44-2.07 22.46-7.84l.8-.57c4.13-2.96 6.42-4.36 9.56-5.56l2.3-.86.25-.1c7.55-2.84 10.8-4.25 13.97-6.3 4.08-2.65 6.16-5.6 6.16-9.27 0-2.89-.97-4.26-3-4.7-1.65-.37-3.05-.25-8.1.5-3.3.5-5.26.7-7.4.7zm112.47-45.34c-1.88 5.44-1.98 6.76-.98 12.76 1.18 7.06-1.38 16.58-5.49 16.58a16.89 16.89 0 0 0-1.51.07l-.64.04c-2.86.18-4.83.17-6.94-.17-6.55-1.06-10.41-5.14-10.41-13.44 0-13.9 2.14-19.69 8.13-26.33a21.9 21.9 0 0 0 2.52-3.75c.59-1.03 2.78-5.13 2.72-5.01 4.44-8.14 7.71-11.53 12.25-10.4 1.17.3 2.2.77 3.58 1.59l1.39.84a20 20 0 0 0 3.1 1.6c.7.27 1.8.32 4.75.26l.72-.01c3.16-.05 4.78.08 5.83.66 1.61.89 1.2 2.56-1.14 4.9a215.9 215.9 0 0 1-3.86 3.76c-10.6 10.1-12.75 12.4-14.02 16.05zm-.94-.32c1.34-3.9 3.46-6.17 14.27-16.46 1.55-1.47 2.73-2.62 3.85-3.73 1.94-1.95 2.17-2.88 1.35-3.33-.82-.45-2.37-.58-5.32-.53l-.72.01c-3.14.06-4.26.02-5.14-.34-1.06-.41-1.97-.9-3.25-1.67l-1.38-.83a12.1 12.1 0 0 0-3.31-1.47c-3.88-.97-6.92 2.17-11.13 9.9.07-.13-2.14 3.98-2.73 5.02a22.71 22.71 0 0 1-2.65 3.92c-5.81 6.47-7.87 12-7.87 25.67 0 7.79 3.48 11.47 9.57 12.45 2.01.33 3.92.34 6.71.16a371.33 371.33 0 0 0 1.23-.07c.42-.03.73-.04.99-.04 3.2 0 5.6-8.9 4.5-15.42-1.02-6.16-.91-7.64 1.03-13.24zm-9.26 12.42c.58.52 2.5 1.9 2.55 1.93 1.96 1.57 2.04 3.31.01 6.36-3.74 5.64-8.83 3.09-8.83-4.55 0-3.81.51-5.67 2.07-6.02 1.18-.26 2 .3 4.2 2.28zm-1.34 1.48c-1.5-1.35-2.23-1.85-2.43-1.8-.17.03-.5 1.23-.5 4.06 0 5.87 2.67 7.21 5.17 3.45 1.5-2.26 1.47-2.84.4-3.7.03.03-1.95-1.4-2.64-2zm222.9-130.19c2.2-1.1 3.67-1.66 5.88-2.36l.28-.09a48.92 48.92 0 0 0 8.79-3.55c4.17-2.08 6.35-1.88 6.96.84.44 2 .2 4.01-1.25 12.7-2.27 13.62-9.16 26.14-21.17 36.3-4.3 3.63-7.41 4.39-9.75 2.44-1.88-1.57-3.1-4.57-4.61-10.48-.3-1.15-1.43-5.83-1.72-6.96a114.18 114.18 0 0 0-2.71-9.22c-2.4-6.82-3.03-10.78-2.1-12.94.77-1.83 2.08-2.24 5.6-2.45 1.49-.09 2.09-.14 2.97-.28l1.95-.33c.72-.12 1.22-.2 1.68-.29 1.1-.2 1.92-.38 2.71-.6 1.7-.49 3.42-1.2 6.49-2.73zm.44.9c-3.11 1.54-4.88 2.29-6.65 2.79-.84.23-1.69.42-2.81.63a108.77 108.77 0 0 1-3.81.63c-.77.13-1.39.19-2.92.28-3.13.18-4.17.51-4.74 1.85-.78 1.84-.2 5.62 2.13 12.2a115.12 115.12 0 0 1 2.74 9.31l1.72 6.96c1.46 5.7 2.62 8.58 4.28 9.96 1.87 1.56 4.49.93 8.47-2.44 11.82-10 18.6-22.3 20.83-35.7 1.4-8.45 1.65-10.51 1.25-12.31-.41-1.87-1.86-2-5.54-.16a49.87 49.87 0 0 1-8.93 3.6l-.28.1a35.4 35.4 0 0 0-5.74 2.3zm-4.5 6.58c1.37-.32 2.5-.75 3.9-1.42.35-.18 2.57-1.31 3.32-1.67 1.5-.71 2.97-1.31 4.7-1.89 2.7-.9 4.64-.77 5.88.4.98.94 1.34 2.26 1.41 4.18.02.4.02.7.02 1.37 0 5.63-4.63 16.88-11.34 22.75-4.34 3.8-7.31 4.67-9.92 2.52-2.06-1.7-3.5-4.65-6.67-12.91-1.86-4.83-2.05-8.1-.68-10.2 1.12-1.7 2.9-2.36 5.83-2.7l1.26-.12c1.19-.12 1.75-.19 2.3-.31zm-2.1 2.3l-1.22.12c-2.4.27-3.7.76-4.39 1.81-.93 1.43-.78 4.1.87 8.38 3.02 7.84 4.41 10.71 6.08 12.09 1.63 1.34 3.64.75 7.33-2.48C584.6 250.77 589 240.08 589 235c0-.64 0-.93-.02-1.29-.05-1.44-.3-2.33-.79-2.8-.6-.57-1.8-.65-3.87.04a37.95 37.95 0 0 0-4.47 1.8c-.72.34-2.93 1.47-3.32 1.66a19.54 19.54 0 0 1-4.3 1.56c-.66.16-1.28.24-2.56.36zm-227.73-88.98c-1.59 4.3-3.54 7.25-7.14 11.4l-2.6 2.97a67.02 67.02 0 0 0-2.63 3.23 46.4 46.4 0 0 0-4.68 7.5c-2.85 5.7-7.14 10.18-12.85 13.89-4.25 2.76-8.25 4.62-15.67 7.59-11.01 4.4-16.43 1.26-27.22-16.4-2.86-4.69-8.8-8.63-17.98-12.66-3-1.33-12.88-5.24-14.43-5.92-4.96-2.18-7.04-3.72-6.42-5.85.67-2.32 5.3-4.05 15.48-6.08 16.63-3.32 26.93-3.82 39.93-3.02 7.9.49 9.67.5 12.74-.26 1.99-.48 3.92-1.3 6-2.6l2.79-1.71c9.86-6.14 12.94-7.96 17.3-9.9 6.03-2.71 10.57-3.32 13.94-1.4 7.2 4.12 7.68 7.7 3.44 19.22zm-1.88-.7c3.95-10.7 3.6-13.26-2.56-16.78-2.66-1.52-6.62-.99-12.12 1.48-4.24 1.9-7.3 3.7-17.07 9.77l-2.79 1.73a22.6 22.6 0 0 1-6.57 2.84c-3.36.81-5.22.8-13.34.3-12.84-.78-22.97-.29-39.41 3-4.9.97-8.45 1.88-10.79 2.75-2.03.76-3.04 1.45-3.17 1.91-.16.57 1.48 1.79 5.3 3.46 1.5.67 11.39 4.58 14.44 5.93 9.52 4.19 15.74 8.3 18.87 13.44 10.35 16.93 14.87 19.56 24.78 15.6 7.3-2.93 11.21-4.75 15.33-7.42 5.42-3.53 9.47-7.75 12.15-13.1 1.44-2.9 3.02-5.4 4.86-7.82a68.95 68.95 0 0 1 2.72-3.33l2.6-2.97c3.46-3.99 5.28-6.75 6.77-10.79zm-6.64-.39c-7.94 12.8-18.53 21.75-33.3 25.23-7.82 1.83-12.47-.79-13.12-5.93-.55-4.45 2.29-9.06 6-9.06 3.02 0 5.6-1.68 15.38-9.16 1.47-1.12 2.57-1.96 3.66-2.74 4.4-3.2 7.77-5.17 10.82-6.08 5.57-1.67 9.33-2.15 11.35-1.22 2.5 1.14 2.22 4.13-.79 8.96zm-.84-.52c2.72-4.4 2.94-6.74 1.21-7.53-1.71-.79-5.32-.33-10.65 1.27-2.9.87-6.2 2.79-10.51 5.92-1.08.79-2.18 1.62-3.65 2.74-10.08 7.72-12.62 9.36-15.98 9.36-3.02 0-5.5 4.02-5 7.94.56 4.5 4.62 6.78 11.89 5.07 14.48-3.4 24.86-12.18 32.69-24.77zM461.17 33.53c13.88 4.96 20.75 4.96 31.62.01 3.02-1.37 5.47-2.94 11-6.82 5.57-3.92 8.05-5.51 11.14-6.92 4.14-1.88 7.78-2.38 11.22-1.28 3.92 1.26 6.2 12.3 6.78 28.45.5 14.2-.52 28.93-2.46 34.2-1.82 4.93-5.86 8.17-11.51 10.02A41.7 41.7 0 0 1 506 93.01c-5.79 0-9 2.4-12.2 7.64-.37.59-1.55 2.6-1.71 2.87-1.75 2.9-3.05 4.33-4.93 4.95-.94.32-2.07.83-3.87 1.74l-2.43 1.23c-1.03.53-1.87.94-2.7 1.34-6.43 3.1-11.73 4.72-17.16 4.72-5.71 0-10.04 2.09-14.02 5.92-1.16 1.11-4.2 4.53-4.63 4.94-2.54 2.44-5.93 4.24-10.85 6.1-1.4.52-5.98 2.13-6.25 2.22l-2.06.78c-.89.36-1.78.63-2.7.81-5.55 1.14-11.14-.54-17.98-4.42-1.27-.73-5.13-3.06-5.76-3.42-2.05-1.16-4.12-1.53-9.09-1.9l-1.73-.15c-4.78-.4-7.68-1.14-10.22-2.97-5-3.61-6.77-7.76-5.65-12.33 1.33-5.42 6.5-11.02 14.85-17.28a169.2 169.2 0 0 1 6.5-4.61c-.33.23 4.33-2.92 5.3-3.6 2.73-1.91 4.8-3.9 12.75-12.04l1.09-1.1c3.49-3.56 5.89-5.89 8.12-7.83 2.9-2.5 4.72-5.95 7.5-13.05l.63-1.61c2.7-6.92 4.28-10 6.87-12.33 1.42-1.28 6.68-6.54 7.93-7.5 3.98-3 8.01-2.73 19.57 1.4zm-.34.94c-11.26-4.02-15-4.28-18.62-1.53-1.19.9-6.4 6.11-7.88 7.43-2.42 2.18-3.96 5.19-6.6 11.95l-.63 1.61c-2.83 7.26-4.72 10.8-7.77 13.45a141.85 141.85 0 0 0-9.16 8.87c-8.02 8.2-10.08 10.2-12.88 12.16-.99.69-5.65 3.84-5.31 3.6-2.5 1.71-4.52 3.13-6.47 4.59-8.17 6.13-13.23 11.6-14.48 16.72-1.02 4.15.58 7.9 5.26 11.27 2.36 1.7 5.11 2.4 9.72 2.8l1.73.13c5.12.4 7.28.78 9.5 2.05.65.36 4.5 2.7 5.76 3.4 6.66 3.78 12.04 5.4 17.29 4.32.86-.17 1.7-.42 2.52-.75a67 67 0 0 1 2.1-.8c.28-.1 4.86-1.7 6.24-2.22 4.8-1.8 8.08-3.56 10.5-5.88.4-.38 3.44-3.8 4.63-4.94 4.16-4 8.72-6.2 14.72-6.2 5.25 0 10.42-1.59 16.73-4.62.82-.4 1.65-.8 2.68-1.33.12-.06 1.93-.99 2.43-1.23 1.84-.93 3-1.46 4-1.8 1.6-.52 2.76-1.82 4.39-4.52l1.7-2.88c3.39-5.5 6.87-8.11 13.07-8.11 4.45 0 8.73-.49 12.64-1.77 5.4-1.76 9.2-4.8 10.9-9.41 1.87-5.11 2.9-19.75 2.39-33.83-.56-15.53-2.81-26.48-6.08-27.52-3.18-1.02-6.57-.55-10.5 1.23-3.02 1.37-5.47 2.94-11 6.83-5.57 3.92-8.05 5.5-11.14 6.92-11.13 5.05-18.26 5.05-32.38.01zM475 55c5.38 0 7.55-.21 9.72-.96 1.26-.43 9.95-4.8 14.88-6.96 1.9-.82 3.56-2.44 6.6-6.04 2.56-3.04 3.19-3.75 4.4-4.84 3.7-3.35 7.07-3.28 10.22 1.23 6.23 8.9 5.61 15.94.07 27.02a71.26 71.26 0 0 0-2.5 5.48c-.32.8-1 2.7-1.09 2.9-.17.45-.34.81-.54 1.17-.63 1.14-1.56 2.21-4.05 4.7-2.4 2.4-5.16 3.27-11.68 4.33-1.81.3-2.2.36-3 .51-6.02 1.1-9.6 2.69-12.24 6.07-3.57 4.59-7.9 7.48-14.98 10.74-.55.24-1.1.5-1.8.8l-1.78.8a60.08 60.08 0 0 0-7.7 3.9c-2.57 1.6-4.79 2.35-9.42 3.46-8.58 2.06-12.28 3.76-17.37 9.36-5.12 5.64-10.17 7.64-16.63 6.7-5.36-.79-10.63-3.01-23.56-9.48-6.3-3.15-6.43-7.78-1.5-13.56 3.38-3.94 3.52-4.06 19.4-16.44 8.12-6.33 12.97-10.57 16.63-14.88 2.53-2.98 4.2-5.73 4.96-8.3 5.5-18.3 12.5-21.98 22.78-15.56 1.95 1.22 6.61 4.55 7.18 4.9 3.36 2.15 6.52 2.95 13 2.95zm0 2c-6.84 0-10.37-.89-14.08-3.26-.63-.4-5.27-3.71-7.16-4.9-9.05-5.65-14.66-2.7-19.8 14.45-.86 2.87-2.67 5.85-5.35 9.01-3.78 4.45-8.7 8.75-16.94 15.17-15.66 12.21-15.86 12.38-19.1 16.16-4.17 4.9-4.09 8 .88 10.48 12.71 6.35 17.89 8.54 22.94 9.28 5.78.84 10.18-.9 14.87-6.06 5.42-5.96 9.45-7.82 18.38-9.96 4.43-1.07 6.5-1.76 8.83-3.22a61.7 61.7 0 0 1 7.94-4.02l1.78-.8 1.78-.8c6.82-3.13 10.91-5.87 14.24-10.14 3-3.87 7-5.64 13.46-6.82.83-.15 1.21-.21 3.04-.51 6.1-1 8.6-1.78 10.58-3.77 2.36-2.36 3.21-3.34 3.72-4.26.15-.27.29-.56.44-.94.06-.15.75-2.06 1.09-2.9.64-1.6 1.45-3.4 2.57-5.64 5.24-10.49 5.8-16.8.07-24.98-2.4-3.44-4.37-3.48-7.24-.89-1.11 1-1.73 1.7-4.22 4.65-3.24 3.85-5.04 5.59-7.32 6.59-4.82 2.1-13.62 6.53-15.03 7.01-2.44.84-4.79 1.07-10.37 1.07zm-12.7 8.6c5.47 3.9 10.34 3.72 18.23.88 5.39-1.94 5.92-2.1 7.7-2.1 2.5-.01 4.21 1.36 5.24 4.46 1.66 4.98-2.32 8.52-12.3 12.68-2.7 1.13-16.25 6.18-20 7.73-7.86 3.24-13.93 6.42-18.87 10.15-13.02 9.84-18.36 11.93-23.71 9.68a24.67 24.67 0 0 1-3.62-1.98l-1.99-1.28a90.4 90.4 0 0 0-2.24-1.4c-3.33-2-2.82-4.28.85-7.34 1.35-1.13 10.66-7.61 13.53-9.91 7.1-5.69 11.91-11.47 14.41-18.34 3.07-8.45 4.89-12.1 6.8-13.39 1.73-1.16 3.36-.53 6.18 1.9.63.56 3.4 3.08 4.11 3.7 1.93 1.7 3.71 3.15 5.67 4.55zm-.6.8c-1.98-1.42-3.79-2.88-5.74-4.6-.73-.64-3.48-3.16-4.1-3.7-2.5-2.16-3.75-2.65-4.97-1.83-1.66 1.11-3.44 4.7-6.42 12.9-2.57 7.07-7.5 12.99-14.72 18.78-2.91 2.33-12.21 8.8-13.52 9.9-3.22 2.68-3.56 4.17-.97 5.72l2.26 1.4 1.99 1.28c1.47.93 2.48 1.5 3.47 1.91 4.9 2.07 9.96.07 22.72-9.56 5.02-3.79 11.15-7 19.1-10.28 3.76-1.55 17.3-6.6 20-7.72 9.5-3.97 13.14-7.2 11.73-11.44-.9-2.71-2.25-3.8-4.3-3.79-1.6 0-2.15.17-7.36 2.05-8.17 2.94-13.34 3.14-19.16-1.01z\'%3E%3C/path%3E%3C/svg%3E'; + + return backgroundImage; + + } + +}; \ No newline at end of file diff --git a/webroot/js/calculator.js b/webroot/js/calculator.js new file mode 100644 index 0000000000..3e76526400 --- /dev/null +++ b/webroot/js/calculator.js @@ -0,0 +1,48 @@ +/** + * FoodCoopShop - The open source software for your foodcoop + * + * Licensed under the GNU Affero General Public License version 3 + * For full copyright and license information, please see LICENSE + * Redistributions of files must retain the above copyright notice. + * + * @since FoodCoopShop 3.6.0 + * @license https://opensource.org/licenses/AGPL-3.0 + * @author Mario Rothauer + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +foodcoopshop.Calculator = { + + init: function(container) { + + let calculatorToggleButton = $(container).find('.calculator-toggle-button'); + let calculatorInput = $(container).find('.calculator-input'); + + $(calculatorToggleButton).on('click', function (e) { + let calculatorInput = $(this).closest(container).find('.calculator-input'); + if (calculatorInput.css('display') == 'none') { + calculatorInput.show(); + calculatorInput.focus(); + } else { + calculatorInput.hide(); + } + }); + + $(calculatorInput).on('keyup', function (e) { + try { + let calculatorOutput = $(this).closest(container).find('.calculator-output'); + let inputVal = $(this).val(); + if (foodcoopshop.LocalizedJs.helper.defaultLocale != 'en_US') { + inputVal = inputVal.replace(/,/g, '.'); + } + let newValue = math.evaluate(inputVal); + newValue = math.format(newValue, {precision: 14}); // 0,7+0,6 = 1,2999999 + calculatorOutput.val(newValue); + } catch(e) { + console.log('error in expression'); + } + }); + } + +}; diff --git a/webroot/js/cart.js b/webroot/js/cart.js index f97400ebf1..c35f7f8c95 100644 --- a/webroot/js/cart.js +++ b/webroot/js/cart.js @@ -234,7 +234,7 @@ foodcoopshop.Cart = { var orderedQuantityInUnitsWrapper = productWrapper.find('.ew.active .quantity-in-units-input-field-wrapper'); if (orderedQuantityInUnitsWrapper.length > 0) { orderedQuantityInUnitsWrapper.removeClass('error'); - orderedQuantityInUnits = foodcoopshop.Helper.getStringAsFloat(orderedQuantityInUnitsWrapper.find('input').val()) * amount; + orderedQuantityInUnits = orderedQuantityInUnitsWrapper.find('input').val() * amount; } var unitName = ''; diff --git a/webroot/js/ckeditor/config-big.js b/webroot/js/ckeditor/config-big.js index 7751274145..7490aadd8b 100644 --- a/webroot/js/ckeditor/config-big.js +++ b/webroot/js/ckeditor/config-big.js @@ -42,5 +42,5 @@ CKEDITOR.editorConfig = function ( config ) { }; -CKEDITOR.timestamp = 'v4.19.1'; // change this string if version is updated in package.json +CKEDITOR.timestamp = 'v4.21.0'; // change this string if version is updated in package.json diff --git a/webroot/js/ckeditor/config-small-with-upload.js b/webroot/js/ckeditor/config-small-with-upload.js index e3be2fa804..fea9dc69c3 100644 --- a/webroot/js/ckeditor/config-small-with-upload.js +++ b/webroot/js/ckeditor/config-small-with-upload.js @@ -37,4 +37,4 @@ CKEDITOR.editorConfig = function ( config ) { }; -CKEDITOR.timestamp = 'v4.19.1'; // change this string if version is updated in package.json +CKEDITOR.timestamp = 'v4.21.0'; // change this string if version is updated in package.json diff --git a/webroot/js/ckeditor/config.js b/webroot/js/ckeditor/config.js index b525fc4ab0..c6d7f1cb42 100644 --- a/webroot/js/ckeditor/config.js +++ b/webroot/js/ckeditor/config.js @@ -29,5 +29,5 @@ CKEDITOR.editorConfig = function ( config ) { }; -CKEDITOR.timestamp = 'v4.19.1'; // change this string if version is updated in package.json +CKEDITOR.timestamp = 'v4.21.0'; // change this string if version is updated in package.json diff --git a/webroot/js/color-mode.js b/webroot/js/color-mode.js new file mode 100644 index 0000000000..82a4f5083b --- /dev/null +++ b/webroot/js/color-mode.js @@ -0,0 +1,76 @@ +/** + * FoodCoopShop - The open source software for your foodcoop + * + * Licensed under the GNU Affero General Public License version 3 + * For full copyright and license information, please see LICENSE + * Redistributions of files must retain the above copyright notice. + * + * @since FoodCoopShop 3.6.0 + * @license https://opensource.org/licenses/AGPL-3.0 + * @author Mario Rothauer + * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com + * @link https://www.foodcoopshop.com + */ + +foodcoopshop.ColorMode = { + + init: function() { + var colorMode = localStorage.getItem('color-mode'); + if (colorMode === 'dark') { + this.enableDarkMode(); + } else { + this.enableLightMode(); + } + this.setBackgroundImage(); + }, + + setBackgroundImage: function() { + var colorMode = this.getColorMode(); + $('body').css('background-image', 'url("' + foodcoopshop.BackgroundImage.getBackgroundImage(colorMode) + '")'); + this.setBackgroundColor(colorMode); + }, + + /** + * in globals.css a color-theme neutral background color (#a9a9a9) is defined + * to avoid showing a very bright empty page when loading page lasts a while + */ + setBackgroundColor: function(colorMode) { + let backgroundColor = '#0f0f0f'; + if (colorMode == 'light') { + backgroundColor = '#e6e6e6'; + } + $('body').css('background-color', backgroundColor); + }, + + initToggle: function() { + $('.color-mode-toggle').on('click', function() { + if ($('body').hasClass('dark')) { + localStorage.setItem('color-mode', 'light'); + foodcoopshop.ColorMode.enableLightMode(); + } else { + localStorage.setItem('color-mode', 'dark'); + foodcoopshop.ColorMode.enableDarkMode(); + } + foodcoopshop.ColorMode.setBackgroundImage(); + }); + }, + + enableLightMode: function() { + $('body').removeClass('dark'); + var icon = $('.color-mode-toggle').find('i'); + icon.removeClass('fas'); + icon.addClass('far'); + }, + + enableDarkMode: function() { + $('body').addClass('dark'); + var icon = $('.color-mode-toggle').find('i'); + icon.removeClass('far'); + icon.addClass('fas'); + }, + + getColorMode: function() { + return $('body').hasClass('dark') ? 'dark' : 'light'; + } + +}; diff --git a/webroot/js/helper.js b/webroot/js/helper.js index 8a1a4ef4e6..4e35310b27 100644 --- a/webroot/js/helper.js +++ b/webroot/js/helper.js @@ -24,6 +24,7 @@ foodcoopshop.Helper = { foodcoopshop.ModalLogout.init(); this.changeOutgoingLinksTargetToBlank(); this.initCookieBanner(); + foodcoopshop.ColorMode.init(); if (!this.isMobile()) { this.initWindowResize(); this.initScrolltopButton(); @@ -33,6 +34,50 @@ foodcoopshop.Helper = { } }, + showLoader: function() { + this.removeLoader(); + $('body').append('
    '); + }, + + removeLoader: function() { + $('#full-page-loader').remove(); + }, + + initShowLoaderOnContentChange: function() { + var allowList = [ + 'a, button', + ]; + var disallowList = [ + foodcoopshop.Cart.disabledButtonsDuringUpdateCartRequest, + '#user-menu a', + '.order-for-different-customer-info a', + '.swiper-button-prev', + '.swiper-button-next', + '.toggle-link', + 'a.calculator-toggle-button', + 'a.as', + 'a[href^="http://"]', + 'a[href^="https://"]', + 'a.sb-toggle-left', + 'a.open-with-modal', + 'a.color-mode-toggle', + 'button.dropdown-toggle', + '#product-search button', + '.modal-content button', + '.modal-content a', + '#flashMessage a' + ]; + $(allowList.join(',')).not(disallowList.join(',')).on('click', function() { + foodcoopshop.Helper.showLoader(); + }); + }, + + isNumeric: function(str) { + if (typeof str != "string") return false // we only process strings! + return !isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)... + !isNaN(parseFloat(str)) // ...and ensure strings of whitespace fail + }, + initRegistrationAsCompany: function() { var isCompanyCheckbox = $('#customers-is-company'); @@ -109,9 +154,9 @@ foodcoopshop.Helper = { lines = []; linesHtml = ''; for(i in groupedOrderDetails[productId]) { - linesHtml = foodcoopshop.LocalizedJs.helper.YouHaveAlredyOrdered01TimesFor2.replaceI18n(0, '"' + groupedOrderDetails[productId][i].product_name + '"'); + linesHtml = foodcoopshop.LocalizedJs.helper.YouHaveAlreadyOrdered01TimesFor2.replaceI18n(0, '"' + groupedOrderDetails[productId][i].product_name + '"'); linesHtml = linesHtml.replaceI18n(1, groupedOrderDetails[productId][i].product_amount); - var formattedPickupDay = new Date(groupedOrderDetails[productId][i].pickup_day).toLocaleDateString(foodcoopshop.LocalizedJs.helper.defaultLocaleInBCP47, { year:"numeric", month:"2-digit", day:"2-digit"}); + var formattedPickupDay = new Date(groupedOrderDetails[productId][i].pickup_day).toLocaleDateString(foodcoopshop.LocalizedJs.helper.defaultLocaleInBCP47, { year:'numeric', month:'2-digit', day:'2-digit'}); linesHtml = linesHtml.replaceI18n(2, formattedPickupDay); lines.push(linesHtml); } @@ -176,28 +221,6 @@ foodcoopshop.Helper = { }); }, - /** - * Returns a function, that, as long as it continues to be invoked, will not - * be triggered. The function will be called after it stops being called for - * N milliseconds. If `immediate` is passed, trigger the function on the - * leading edge, instead of the trailing. - * https://davidwalsh.name/javascript-debounce-function - */ - debounce: function(func, wait, immediate) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; - }, - addPrevAndNextCategoryLinks : function() { this.addPrevAndNextLinks( '#categories-menu li a', @@ -325,23 +348,6 @@ foodcoopshop.Helper = { }); }, - getJqueryUiNoButton : function() { - return this.getJqueryUiCloseDialogButton(foodcoopshop.LocalizedJs.helper.no); - }, - - getJqueryUiCancelButton : function() { - return this.getJqueryUiCloseDialogButton(foodcoopshop.LocalizedJs.helper.cancel); - }, - - getJqueryUiCloseDialogButton : function(label) { - return { - text: label, - click: function() { - $(this).dialog('close'); - } - }; - }, - initBlogPostCarousel: function () { var selector = '.blog-wrapper'; @@ -734,8 +740,8 @@ foodcoopshop.Helper = { }, - setCakeServerName: function (cakeServerName) { - this.cakeServerName = cakeServerName; + setFullBaseUrl: function (fullBaseUrl) { + this.fullBaseUrl = fullBaseUrl; }, setIsManufacturer: function (isManufacturer) { @@ -752,7 +758,7 @@ foodcoopshop.Helper = { initAnystretch: function () { $.backstretch( - '/img/bg-v3.5.jpg', + '/img/bg-v3.6.jpg', { positionY: 'top', transitionDuration: 400 @@ -769,7 +775,7 @@ foodcoopshop.Helper = { $(this).not('.tooltipstered').tooltipster({ contentAsHTML: true, interactive: true, - maxWidth: 400, + maxWidth: 450, distance: 0, trigger: trigger, animationDuration: 0, @@ -783,17 +789,6 @@ foodcoopshop.Helper = { return imageSrc.replace(/\?.{3}/g, ''); }, - initJqueryUiIcons: function () { - $('li.ui-state-default').hover( - function () { - $(this).addClass('ui-state-hover'); - }, - function () { - $(this).removeClass('ui-state-hover'); - } - ); - }, - showContent: function () { // do not use jquery .animate() or .show() here, if loaded in iframe and firefox, this does not work // only css('display') works @@ -810,7 +805,7 @@ foodcoopshop.Helper = { this.destroyCkeditor(name); - CKEDITOR.timestamp = 'v4.19.1'; + CKEDITOR.timestamp = 'v4.21.0'; $('textarea#' + name + '.ckeditor').ckeditor({ customConfig: '/js/ckeditor/config.js', startupFocus : startupFocus @@ -839,7 +834,7 @@ foodcoopshop.Helper = { this.destroyCkeditor(name); - CKEDITOR.timestamp = 'v4.19.1'; + CKEDITOR.timestamp = 'v4.21.0'; $('textarea#' + name + '.ckeditor').ckeditor({ customConfig: '/js/ckeditor/config-big.js' }); @@ -854,7 +849,7 @@ foodcoopshop.Helper = { this.destroyCkeditor(name); - CKEDITOR.timestamp = 'v4.19.1'; + CKEDITOR.timestamp = 'v4.21.0'; $('textarea#' + name + '.ckeditor').ckeditor({ customConfig: '/js/ckeditor/config-small-with-upload.js' }); @@ -947,10 +942,6 @@ foodcoopshop.Helper = { }, obj || self); }, - getRandomCode: function () { - return Math.floor(Math.random() * 981151510); - }, - removeFlashMessage: function () { $('#flashMessage').remove(); }, @@ -980,7 +971,7 @@ foodcoopshop.Helper = { duration: duration, easing: 'linear', } - ); + ); $('#flashMessage.success .progress-bar.bg-white') .animate({ 'width': '0%', @@ -989,7 +980,7 @@ foodcoopshop.Helper = { duration: duration, easing: 'linear', } - ); + ); setTimeout(function() { $('#flashMessage.success a.closer').trigger('click'); @@ -1063,6 +1054,12 @@ foodcoopshop.Helper = { ajaxCall: function (url, data, callbacks) { + var csrfToken = $('meta[name="csrfToken"]').attr('content'); + jQuery.ajaxSetup({ + headers: + { 'X-CSRF-TOKEN': csrfToken } + }); + return jQuery.ajax({ url: url, type: callbacks.method || 'POST', diff --git a/webroot/js/self-service.js b/webroot/js/self-service.js index 677014f193..539fea2b86 100644 --- a/webroot/js/self-service.js +++ b/webroot/js/self-service.js @@ -18,6 +18,7 @@ foodcoopshop.SelfService = { init : function() { foodcoopshop.ModalLogout.init(document.location.href); + foodcoopshop.ColorMode.init(); this.initWindowResize(); this.initSearchForm(); this.bindQuantityInUnitsInputFields(); diff --git a/webroot/package-lock.json b/webroot/package-lock.json index 269cd4db49..e366a7fcd7 100644 --- a/webroot/package-lock.json +++ b/webroot/package-lock.json @@ -8,18 +8,18 @@ "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { - "@beyonk/gdpr-cookie-consent-banner": "^9.0.3", - "@ericblade/quagga2": "^1.7.1", - "@fortawesome/fontawesome-free": "^6.2.0", + "@beyonk/gdpr-cookie-consent-banner": "^9.1.1", + "@ericblade/quagga2": "^1.8.2", + "@fortawesome/fontawesome-free": "^6.4.0", "blueimp-file-upload": "^10.32.0", - "bootstrap": "^5.2.1", + "bootstrap": "^5.2.3", "bootstrap-select": "^1.14.0-beta3", - "chart.js": "^3.9.1", - "chartjs-plugin-datalabels": "^2.1.0", - "ckeditor4": "^4.19.1", - "clean-css-cli": "^5.6.1", + "chart.js": "^4.2.1", + "chartjs-plugin-datalabels": "^2.2.0", + "ckeditor4": "^4.21.0", + "clean-css-cli": "^5.6.2", "clipboard": "^2.0.11", - "jquery": "^3.6.1", + "jquery": "^3.6.4", "jquery-backstretch": "^2.1.18", "jquery-knob": "~1.2.11", "jquery-ui": "^1.13.2", @@ -27,13 +27,13 @@ "jquery.scrollto": "^2.1.3", "js-cookie": "^3.0.1", "lazysizes": "^5.3.2", + "mathjs": "^11.8.0", "scrolltofixed": "^1.0.6", "slidebars": "^2.0.2", - "svelte": "^3.50.0", - "swiper": "8.3.2", + "swiper": "9.2.0", "tooltipster": "^4.2.8", - "uglify-js": "^3.17.0", - "webrtc-adapter": "^8.1.2" + "uglify-js": "^3.17.4", + "webrtc-adapter": "^8.2.2" } }, "node_modules/@babel/polyfill": { @@ -45,23 +45,33 @@ "regenerator-runtime": "^0.13.4" } }, + "node_modules/@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@beyonk/gdpr-cookie-consent-banner": { - "version": "9.0.3", - "integrity": "sha512-9rno8oVhhMZNWMslkfOISr31pYZy5OKEr8393nUn7DdLRNveu2r6eoNeeTabY2WSzWagySt+IIrspHxT3PAqXQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@beyonk/gdpr-cookie-consent-banner/-/gdpr-cookie-consent-banner-9.1.1.tgz", + "integrity": "sha512-l3ltb21B1isHzu9LOoPNiyqtWUoz3oRspzeAgSFJsK9rOBa5U7sWv0PbXJS8EenAh4aBsQiCagCTUyPJtNw9lQ==", "dependencies": { "js-cookie": "^3.0.1" } }, "node_modules/@ericblade/quagga2": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@ericblade/quagga2/-/quagga2-1.7.1.tgz", - "integrity": "sha512-brx0N6MHTCgsFE/b5EiMVKguUpBhddmKwpgxXDUpVEthw4n6v3OWE+EkI1SgYYUJn/MPqy6B/QwTARcymm9O6w==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@ericblade/quagga2/-/quagga2-1.8.2.tgz", + "integrity": "sha512-UCVC6nnE0z8sjdpJqtIHJDqRdVMdK3oJdDx0O1LhBK+eKqm01upTGEEFo2lglsAaW1cVrnxWEcwHmUnNUP0ukg==", "dependencies": { "@babel/polyfill": "^7.12.1", "get-pixels": "^3.3.3", - "gl-mat2": "^1.0.1", - "gl-vec2": "^1.3.0", - "gl-vec3": "^1.1.3", + "gl-matrix": "^3.4.3", "lodash": "^4.17.21", "ndarray": "^1.0.19", "ndarray-linear-interpolate": "^1.0.0" @@ -70,18 +80,23 @@ "node": ">= 10.0" }, "optionalDependencies": { - "fsevents": "2.1.2" + "fsevents": "2.3.2" } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.0.tgz", - "integrity": "sha512-CNR7qRIfCwWHNN7FnKUniva94edPdyQzil/zCwk3v6k4R6rR2Fr8i4s3PM7n/lyfPA6Zfko9z5WDzFxG9SW1uQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", + "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==", "hasInstallScript": true, "engines": { "node": ">=6" } }, + "node_modules/@kurkle/color": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.1.tgz", + "integrity": "sha512-hW0GwZj06z/ZFUW2Espl7toVDjghJN+EKqyXzPSV8NV89d5BYp5rRMBJoc+aUN0x5OXDMeRQHazejr2Xmqj2tw==" + }, "node_modules/@popperjs/core": { "version": "2.11.6", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", @@ -198,9 +213,9 @@ } }, "node_modules/bootstrap": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.1.tgz", - "integrity": "sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", "funding": [ { "type": "github", @@ -249,16 +264,22 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "node_modules/chart.js": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", - "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz", + "integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": "^7.0.0" + } }, "node_modules/chartjs-plugin-datalabels": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.1.0.tgz", - "integrity": "sha512-WA6R4saSlY6mnyX78SkbSo2gGc+cj87lFi5zBrsjjYxE76JgXyxHa1OTodVCzRPoqeYJqSEOffeJ/897kRHR6w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz", + "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==", "peerDependencies": { - "chart.js": "^3.0.0" + "chart.js": ">=3.0.0" } }, "node_modules/chokidar": { @@ -287,28 +308,15 @@ "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/ckeditor4": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/ckeditor4/-/ckeditor4-4.19.1.tgz", - "integrity": "sha512-eK/tilHSUpLc9mrD7Lvt07LJfJ13BBa4ftxJBxIX49sGMlWg5WaB81C+MRBZwnntcfpjVdyCLpZAptAHTDB65Q==" + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/ckeditor4/-/ckeditor4-4.21.0.tgz", + "integrity": "sha512-OAMw68puJcrKFtsPZwIWVB/upYLgJpFw1yTuBBIhoreY+g/f0SttjQY0I/fUwxevVUHvgmRVNeJwNl8qkJPvyw==" }, "node_modules/clean-css": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", - "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", "dependencies": { "source-map": "~0.6.0" }, @@ -317,12 +325,12 @@ } }, "node_modules/clean-css-cli": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.1.tgz", - "integrity": "sha512-/StJu1YODZY6cOwkBx5FMhSoc9YmvEJXtwNN+udGg1GIKrr4PkdsCdUqC26GfdPdt5IuZnu+5y9/3mrdIJa40Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.2.tgz", + "integrity": "sha512-GDQkr6zVqHJhO3yWTy3sA22sMCT6iUqaJuBdqZMW6oI25MtiJ2iZXDmWzErpjoRotsB+TYPTpuZSNSgaC1n4lA==", "dependencies": { "chokidar": "^3.5.2", - "clean-css": "^5.3.1", + "clean-css": "^5.3.2", "commander": "7.x", "glob": "^7.1.6" }, @@ -361,6 +369,18 @@ "node": ">= 10" } }, + "node_modules/complex.js": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -397,6 +417,11 @@ "version": "0.0.3", "integrity": "sha1-GK6XmmoMqZSwYlhTkW0mYruuCxo=" }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, "node_modules/delayed-stream": { "version": "1.0.0", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", @@ -408,13 +433,6 @@ "version": "3.2.0", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "node_modules/dom7": { - "version": "4.0.4", - "integrity": "sha512-DSSgBzQ4rJWQp1u6o+3FVwMNnT5bzQbMb+o31TjYYeRi05uAcpF8koxdfzeoe5ElzPmua7W7N28YJhF7iEKqIw==", - "dependencies": { - "ssr-window": "^4.0.0" - } - }, "node_modules/ecc-jsbn": { "version": "0.1.2", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", @@ -423,6 +441,11 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "node_modules/extend": { "version": "3.0.2", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" @@ -472,16 +495,27 @@ "node": ">= 0.12" } }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "hasInstallScript": true, "optional": true, "os": [ @@ -515,17 +549,10 @@ "assert-plus": "^1.0.0" } }, - "node_modules/gl-mat2": { - "version": "1.0.1", - "integrity": "sha1-FCUFcwpcL+Hp8l2ezj0NbMJxCjA=" - }, - "node_modules/gl-vec2": { - "version": "1.3.0", - "integrity": "sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A==" - }, - "node_modules/gl-vec3": { - "version": "1.1.3", - "integrity": "sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw==" + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" }, "node_modules/glob": { "version": "7.2.3", @@ -664,15 +691,20 @@ "version": "0.1.2", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, "node_modules/jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" }, "node_modules/jquery": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", - "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz", + "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==" }, "node_modules/jquery-backstretch": { "version": "2.1.18", @@ -753,6 +785,28 @@ "version": "4.17.21", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/mathjs": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.8.0.tgz", + "integrity": "sha512-I7r8HCoqUGyEiHQdeOCF2m2k9N+tcOHO3cZQ3tyJkMMBQMFqMR7dMQEboBMJAiFW2Um3PEItGPwcOc4P6KRqwg==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "complex.js": "^2.1.1", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.1.0" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/mime-db": { "version": "1.49.0", "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", @@ -902,8 +956,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.7", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/request": { "version": "2.88.2", @@ -962,8 +1017,14 @@ "integrity": "sha1-LbIi8eqheLeBTtSeZDVrgCW7yiU=" }, "node_modules/sdp": { - "version": "3.0.2", - "integrity": "sha512-boRjiof+/Ca8kq0dFgHTs9HD3/a5rCAJLDCi9DTFyTq+PerwPXkffVgAyLWAL0KGp9ZpkH1o8GV+vz8UehdPBQ==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "node_modules/select": { "version": "1.1.2", @@ -1009,20 +1070,13 @@ }, "node_modules/ssr-window": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz", "integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==" }, - "node_modules/svelte": { - "version": "3.50.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.50.0.tgz", - "integrity": "sha512-zXeOUDS7+85i+RxLN+0iB6PMbGH7OhEgjETcD1fD8ZrhuhNFxYxYEHU41xuhkHIulJavcu3PKbPyuCrBxdxskQ==", - "engines": { - "node": ">= 8" - } - }, "node_modules/swiper": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/swiper/-/swiper-8.3.2.tgz", - "integrity": "sha512-8wsC7ORYvVSnLUoxs2+xmfLrDPNjBVQXMCFbOlqtHeON6wtu/blOyySDr8TCBCdse1bdcIbn7m8xJNxVFL8o4Q==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-9.2.0.tgz", + "integrity": "sha512-lWK9toYumUQss+YuTL+Mt0+8twiMJEyzioER4bbS4rrGHlkeLrDM8uhtAmnpdijELrNscuNUujDgKoMQZfQGlQ==", "funding": [ { "type": "patreon", @@ -1033,9 +1087,7 @@ "url": "http://opencollective.com/swiper" } ], - "hasInstallScript": true, "dependencies": { - "dom7": "^4.0.4", "ssr-window": "^4.0.2" }, "engines": { @@ -1093,10 +1145,18 @@ "version": "0.14.5", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "node_modules/typed-function": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.0.tgz", + "integrity": "sha512-DGwUl6cioBW5gw2L+6SMupGwH/kZOqivy17E4nsh1JI9fKF87orMmlQx3KISQPmg3sfnOUGlwVkroosvgddrlg==", + "engines": { + "node": ">= 14" + } + }, "node_modules/uglify-js": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz", - "integrity": "sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==", + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -1136,11 +1196,11 @@ } }, "node_modules/webrtc-adapter": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.1.2.tgz", - "integrity": "sha512-j1tsxKR/NmNgqrlLTL5jsNmFBrsIdTvBWZ2I1UAs/J37M1s1chLy1Fp7RfQHflHk3KoSNAxp/4y6ictHJ8prSw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.2.tgz", + "integrity": "sha512-jQWwqiAEAFZamWliJo0Q+dIC6ZMJ8BgCFvW/oXWVFby1Nw14dOUfPwZ3lVe4nafDXdTyCUT7xfLt5xXiioXUCQ==", "dependencies": { - "sdp": "^3.0.2" + "sdp": "^3.2.0" }, "engines": { "node": ">=6.0.0", @@ -1162,33 +1222,45 @@ "regenerator-runtime": "^0.13.4" } }, + "@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@beyonk/gdpr-cookie-consent-banner": { - "version": "9.0.3", - "integrity": "sha512-9rno8oVhhMZNWMslkfOISr31pYZy5OKEr8393nUn7DdLRNveu2r6eoNeeTabY2WSzWagySt+IIrspHxT3PAqXQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@beyonk/gdpr-cookie-consent-banner/-/gdpr-cookie-consent-banner-9.1.1.tgz", + "integrity": "sha512-l3ltb21B1isHzu9LOoPNiyqtWUoz3oRspzeAgSFJsK9rOBa5U7sWv0PbXJS8EenAh4aBsQiCagCTUyPJtNw9lQ==", "requires": { "js-cookie": "^3.0.1" } }, "@ericblade/quagga2": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@ericblade/quagga2/-/quagga2-1.7.1.tgz", - "integrity": "sha512-brx0N6MHTCgsFE/b5EiMVKguUpBhddmKwpgxXDUpVEthw4n6v3OWE+EkI1SgYYUJn/MPqy6B/QwTARcymm9O6w==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@ericblade/quagga2/-/quagga2-1.8.2.tgz", + "integrity": "sha512-UCVC6nnE0z8sjdpJqtIHJDqRdVMdK3oJdDx0O1LhBK+eKqm01upTGEEFo2lglsAaW1cVrnxWEcwHmUnNUP0ukg==", "requires": { "@babel/polyfill": "^7.12.1", - "fsevents": "2.1.2", + "fsevents": "2.3.2", "get-pixels": "^3.3.3", - "gl-mat2": "^1.0.1", - "gl-vec2": "^1.3.0", - "gl-vec3": "^1.1.3", + "gl-matrix": "^3.4.3", "lodash": "^4.17.21", "ndarray": "^1.0.19", "ndarray-linear-interpolate": "^1.0.0" } }, "@fortawesome/fontawesome-free": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.0.tgz", - "integrity": "sha512-CNR7qRIfCwWHNN7FnKUniva94edPdyQzil/zCwk3v6k4R6rR2Fr8i4s3PM7n/lyfPA6Zfko9z5WDzFxG9SW1uQ==" + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", + "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==" + }, + "@kurkle/color": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.1.tgz", + "integrity": "sha512-hW0GwZj06z/ZFUW2Espl7toVDjghJN+EKqyXzPSV8NV89d5BYp5rRMBJoc+aUN0x5OXDMeRQHazejr2Xmqj2tw==" }, "@popperjs/core": { "version": "2.11.6", @@ -1280,9 +1352,9 @@ "optional": true }, "bootstrap": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.1.tgz", - "integrity": "sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", "requires": {} }, "bootstrap-select": { @@ -1313,14 +1385,17 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chart.js": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", - "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz", + "integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==", + "requires": { + "@kurkle/color": "^0.3.0" + } }, "chartjs-plugin-datalabels": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.1.0.tgz", - "integrity": "sha512-WA6R4saSlY6mnyX78SkbSo2gGc+cj87lFi5zBrsjjYxE76JgXyxHa1OTodVCzRPoqeYJqSEOffeJ/897kRHR6w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz", + "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==", "requires": {} }, "chokidar": { @@ -1336,36 +1411,28 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" - }, - "dependencies": { - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - } } }, "ckeditor4": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/ckeditor4/-/ckeditor4-4.19.1.tgz", - "integrity": "sha512-eK/tilHSUpLc9mrD7Lvt07LJfJ13BBa4ftxJBxIX49sGMlWg5WaB81C+MRBZwnntcfpjVdyCLpZAptAHTDB65Q==" + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/ckeditor4/-/ckeditor4-4.21.0.tgz", + "integrity": "sha512-OAMw68puJcrKFtsPZwIWVB/upYLgJpFw1yTuBBIhoreY+g/f0SttjQY0I/fUwxevVUHvgmRVNeJwNl8qkJPvyw==" }, "clean-css": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", - "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", "requires": { "source-map": "~0.6.0" } }, "clean-css-cli": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.1.tgz", - "integrity": "sha512-/StJu1YODZY6cOwkBx5FMhSoc9YmvEJXtwNN+udGg1GIKrr4PkdsCdUqC26GfdPdt5IuZnu+5y9/3mrdIJa40Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-5.6.2.tgz", + "integrity": "sha512-GDQkr6zVqHJhO3yWTy3sA22sMCT6iUqaJuBdqZMW6oI25MtiJ2iZXDmWzErpjoRotsB+TYPTpuZSNSgaC1n4lA==", "requires": { "chokidar": "^3.5.2", - "clean-css": "^5.3.1", + "clean-css": "^5.3.2", "commander": "7.x", "glob": "^7.1.6" } @@ -1392,6 +1459,11 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" }, + "complex.js": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.1.1.tgz", + "integrity": "sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1423,6 +1495,11 @@ "version": "0.0.3", "integrity": "sha1-GK6XmmoMqZSwYlhTkW0mYruuCxo=" }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, "delayed-stream": { "version": "1.0.0", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" @@ -1431,13 +1508,6 @@ "version": "3.2.0", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "dom7": { - "version": "4.0.4", - "integrity": "sha512-DSSgBzQ4rJWQp1u6o+3FVwMNnT5bzQbMb+o31TjYYeRi05uAcpF8koxdfzeoe5ElzPmua7W7N28YJhF7iEKqIw==", - "requires": { - "ssr-window": "^4.0.0" - } - }, "ecc-jsbn": { "version": "0.1.2", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", @@ -1446,6 +1516,11 @@ "safer-buffer": "^2.1.0" } }, + "escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "extend": { "version": "3.0.2", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" @@ -1483,15 +1558,20 @@ "mime-types": "^2.1.12" } }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "optional": true }, "get-pixels": { @@ -1518,17 +1598,10 @@ "assert-plus": "^1.0.0" } }, - "gl-mat2": { - "version": "1.0.1", - "integrity": "sha1-FCUFcwpcL+Hp8l2ezj0NbMJxCjA=" - }, - "gl-vec2": { - "version": "1.3.0", - "integrity": "sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A==" - }, - "gl-vec3": { - "version": "1.1.3", - "integrity": "sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw==" + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" }, "glob": { "version": "7.2.3", @@ -1635,15 +1708,20 @@ "version": "0.1.2", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, "jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" }, "jquery": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", - "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz", + "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==" }, "jquery-backstretch": { "version": "2.1.18", @@ -1716,6 +1794,22 @@ "version": "4.17.21", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "mathjs": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-11.8.0.tgz", + "integrity": "sha512-I7r8HCoqUGyEiHQdeOCF2m2k9N+tcOHO3cZQ3tyJkMMBQMFqMR7dMQEboBMJAiFW2Um3PEItGPwcOc4P6KRqwg==", + "requires": { + "@babel/runtime": "^7.21.0", + "complex.js": "^2.1.1", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "^4.2.0", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.1.0" + } + }, "mime-db": { "version": "1.49.0", "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" @@ -1826,8 +1920,9 @@ } }, "regenerator-runtime": { - "version": "0.13.7", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "request": { "version": "2.88.2", @@ -1868,8 +1963,14 @@ "integrity": "sha1-LbIi8eqheLeBTtSeZDVrgCW7yiU=" }, "sdp": { - "version": "3.0.2", - "integrity": "sha512-boRjiof+/Ca8kq0dFgHTs9HD3/a5rCAJLDCi9DTFyTq+PerwPXkffVgAyLWAL0KGp9ZpkH1o8GV+vz8UehdPBQ==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" }, "select": { "version": "1.1.2", @@ -1904,19 +2005,14 @@ }, "ssr-window": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz", "integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==" }, - "svelte": { - "version": "3.50.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.50.0.tgz", - "integrity": "sha512-zXeOUDS7+85i+RxLN+0iB6PMbGH7OhEgjETcD1fD8ZrhuhNFxYxYEHU41xuhkHIulJavcu3PKbPyuCrBxdxskQ==" - }, "swiper": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/swiper/-/swiper-8.3.2.tgz", - "integrity": "sha512-8wsC7ORYvVSnLUoxs2+xmfLrDPNjBVQXMCFbOlqtHeON6wtu/blOyySDr8TCBCdse1bdcIbn7m8xJNxVFL8o4Q==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-9.2.0.tgz", + "integrity": "sha512-lWK9toYumUQss+YuTL+Mt0+8twiMJEyzioER4bbS4rrGHlkeLrDM8uhtAmnpdijELrNscuNUujDgKoMQZfQGlQ==", "requires": { - "dom7": "^4.0.4", "ssr-window": "^4.0.2" } }, @@ -1960,10 +2056,15 @@ "version": "0.14.5", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "typed-function": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.1.0.tgz", + "integrity": "sha512-DGwUl6cioBW5gw2L+6SMupGwH/kZOqivy17E4nsh1JI9fKF87orMmlQx3KISQPmg3sfnOUGlwVkroosvgddrlg==" + }, "uglify-js": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz", - "integrity": "sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==" + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==" }, "uniq": { "version": "1.0.1", @@ -1990,11 +2091,11 @@ } }, "webrtc-adapter": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.1.2.tgz", - "integrity": "sha512-j1tsxKR/NmNgqrlLTL5jsNmFBrsIdTvBWZ2I1UAs/J37M1s1chLy1Fp7RfQHflHk3KoSNAxp/4y6ictHJ8prSw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.2.tgz", + "integrity": "sha512-jQWwqiAEAFZamWliJo0Q+dIC6ZMJ8BgCFvW/oXWVFby1Nw14dOUfPwZ3lVe4nafDXdTyCUT7xfLt5xXiioXUCQ==", "requires": { - "sdp": "^3.0.2" + "sdp": "^3.2.0" } }, "wrappy": { diff --git a/webroot/package.json b/webroot/package.json index ed5371c42e..b29b330944 100644 --- a/webroot/package.json +++ b/webroot/package.json @@ -11,21 +11,21 @@ "license": "AGPL-3.0", "homepage": "https://www.foodcoopshop.com", "scripts": { - "install": "bash ../bin/cake npm_post_install" + "install": "bash ../devtools/npm-post-install.sh" }, "dependencies": { - "@beyonk/gdpr-cookie-consent-banner": "^9.0.3", - "@ericblade/quagga2": "^1.7.1", - "@fortawesome/fontawesome-free": "^6.2.0", + "@beyonk/gdpr-cookie-consent-banner": "^9.1.1", + "@ericblade/quagga2": "^1.8.2", + "@fortawesome/fontawesome-free": "^6.4.0", "blueimp-file-upload": "^10.32.0", - "bootstrap": "^5.2.1", + "bootstrap": "^5.2.3", "bootstrap-select": "^1.14.0-beta3", - "chart.js": "^3.9.1", - "chartjs-plugin-datalabels": "^2.1.0", - "ckeditor4": "^4.19.1", - "clean-css-cli": "^5.6.1", + "chart.js": "^4.2.1", + "chartjs-plugin-datalabels": "^2.2.0", + "ckeditor4": "^4.21.0", + "clean-css-cli": "^5.6.2", "clipboard": "^2.0.11", - "jquery": "^3.6.1", + "jquery": "^3.6.4", "jquery-backstretch": "^2.1.18", "jquery-knob": "~1.2.11", "jquery-ui": "^1.13.2", @@ -33,12 +33,12 @@ "jquery.scrollto": "^2.1.3", "js-cookie": "^3.0.1", "lazysizes": "^5.3.2", + "mathjs": "^11.8.0", "scrolltofixed": "^1.0.6", "slidebars": "^2.0.2", - "svelte": "^3.50.0", - "swiper": "8.3.2", + "swiper": "9.2.0", "tooltipster": "^4.2.8", - "uglify-js": "^3.17.0", - "webrtc-adapter": "^8.1.2" + "uglify-js": "^3.17.4", + "webrtc-adapter": "^8.2.2" } }