';
}
- $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 .= '';
+ $pdf->table .= ' '; //spacer left of barcode
+ $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 .= ' '; //spacer between barcode and customer image
// move user image to bottom
- $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(); ?>
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 = '';
foreach($products as $product) {
$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
@@ -40,6 +42,7 @@
// START ROW barcode and product image
$pdf->table .= '';
+ $pdf->table .= ' '; //spacer left of barcode
$pdf->table .= '';
$barcodeObject = new TCPDFBarcode($product->system_bar_code, 'C39');
//https://stackoverflow.com/a/54520065/2100184
@@ -50,7 +53,7 @@
$pdf->table .= ' ';
$pdf->table .= ' '; //spacer between barcode and customer image
// move user image to bottom
- $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-Hersteller ');
// 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('Demo Fleisch-Hersteller ');
$this->assertResponseContains('Demo Gemüse-Hersteller ');
$this->assertResponseContains('Demo Milch-Hersteller ');
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 += '' + foodcoopshop.LocalizedJs.admin.WhyIsMemberEdited + ' ';
html += '';
html += '
';
+ html += '';
+ html += ' ';
+ html += '' + foodcoopshop.LocalizedJs.admin.SendEmailToBothMembers + ' ';
+ 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 += '';
+ html += '';
- html += '';
- html += ' ';
- html += '' + foodcoopshop.LocalizedJs.admin.DoNotAutomaticallyAdaptPriceJustChangeWeight + ' ';
- html += ' ';
- html += ' ';
return html;
},
getCloseHandler : function() {
$('#dialogOrderDetailProductQuantityQuantity').val('');
$('#dialogOrderDetailProductQuantityOrderDetailId').val('');
- $('#dialogOrderDetailProductQuantityDoNotChangePrice').prop('checked', false);
+ $('#dialogOrderDetailProductQuantityCalculator').val('').hide();
$('#flashMessage').remove();
},
@@ -74,8 +77,7 @@ foodcoopshop.ModalOrderDetailProductQuantityEdit = {
'/admin/order-details/editProductQuantity/',
{
orderDetailId: $('#dialogOrderDetailProductQuantityOrderDetailId').val(),
- productQuantity: productQuantity,
- doNotChangePrice: $('#dialogOrderDetailProductQuantityDoNotChangePrice:checked').length > 0 ? 1 : 0
+ productQuantity: productQuantity
},
{
onOk: function (data) {
diff --git a/plugins/Admin/webroot/js/modal/modal-order-for-different-customer-add.js b/plugins/Admin/webroot/js/modal/modal-order-for-different-customer-add.js
index 90680fcfa3..a850e7afd2 100644
--- a/plugins/Admin/webroot/js/modal/modal-order-for-different-customer-add.js
+++ b/plugins/Admin/webroot/js/modal/modal-order-for-different-customer-add.js
@@ -83,7 +83,7 @@ foodcoopshop.ModalIOrderForDifferentCustomerAdd = {
// always preselect user if there is a dropdown called #customerId (for call from order detail)
var customerId = $('#customerid').val();
foodcoopshop.Admin.initCustomerDropdown(customerId, 0, 0, customerDropdownSelector, function () {
- var newSrc = foodcoopshop.Helper.cakeServerName + iframeSrcInit + '/' + $(customerDropdownSelector).val();
+ var newSrc = foodcoopshop.Helper.fullBaseUrl + iframeSrcInit + '/' + $(customerDropdownSelector).val();
$(modalSelector + ' iframe').attr('src', newSrc);
});
@@ -92,7 +92,7 @@ foodcoopshop.ModalIOrderForDifferentCustomerAdd = {
// START IFRAME
var iframe = $('');
- iframe.attr('src', foodcoopshop.Helper.cakeServerName + iframeSrc);
+ iframe.attr('src', foodcoopshop.Helper.fullBaseUrl + iframeSrc);
iframe.css('width', '100%');
iframe.css('border', 'none');
$(modalSelector + ' .modal-body').append(iframe);
diff --git a/plugins/Admin/webroot/js/modal/modal-product-add.js b/plugins/Admin/webroot/js/modal/modal-product-add.js
index 664f0854e2..47c1ab5bda 100644
--- a/plugins/Admin/webroot/js/modal/modal-product-add.js
+++ b/plugins/Admin/webroot/js/modal/modal-product-add.js
@@ -45,8 +45,8 @@ foodcoopshop.ModalProductAdd = {
if ($('.storage-location-dropdown-wrapper').length > 0) {
html += ' ';
html += '';
- html += '' + foodcoopshop.LocalizedJs.dialogProduct.StorageLocation + ' ';
- html += ' ';
+ html += '' + foodcoopshop.LocalizedJs.dialogProduct.StorageLocation + ' ';
+ html += ' ';
html += '
';
}
diff --git a/plugins/Admin/webroot/js/modal/modal-product-calculate-selling-price-with-surcharge.js b/plugins/Admin/webroot/js/modal/modal-product-calculate-selling-price-with-surcharge.js
index 4369e242ef..78c21782f4 100644
--- a/plugins/Admin/webroot/js/modal/modal-product-calculate-selling-price-with-surcharge.js
+++ b/plugins/Admin/webroot/js/modal/modal-product-calculate-selling-price-with-surcharge.js
@@ -20,7 +20,7 @@ foodcoopshop.ModalProductCalculateSellingPriceWithSurcharge = {
var button = $('#calculateSellingPriceWithSurchargForSelectedProducts');
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-product-delete.js b/plugins/Admin/webroot/js/modal/modal-product-delete.js
index a71e983857..db6ac95040 100644
--- a/plugins/Admin/webroot/js/modal/modal-product-delete.js
+++ b/plugins/Admin/webroot/js/modal/modal-product-delete.js
@@ -20,7 +20,7 @@ foodcoopshop.ModalProductDelete = {
var button = $('#deleteSelectedProducts');
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-product-delivery-rhythm-edit.js b/plugins/Admin/webroot/js/modal/modal-product-delivery-rhythm-edit.js
index a63c43bac4..9fb752e0f0 100644
--- a/plugins/Admin/webroot/js/modal/modal-product-delivery-rhythm-edit.js
+++ b/plugins/Admin/webroot/js/modal/modal-product-delivery-rhythm-edit.js
@@ -18,7 +18,7 @@ foodcoopshop.ModalProductDeliveryRhythmEdit = {
var button = $('#editDeliveryRhythmForSelectedProducts');
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);
});
@@ -119,7 +119,7 @@ foodcoopshop.ModalProductDeliveryRhythmEdit = {
select.append($('#rhythmtypes').html());
select.on('change', function() {
var elementToShow = 'default';
- if ($(this).val().match('individual')) {
+ if ($(this).val() !== null && $(this).val().match('individual')) {
elementToShow = 'individual';
}
$(modalSelector + ' .dynamic-element').hide();
diff --git a/plugins/Admin/webroot/js/modal/modal-product-name-edit.js b/plugins/Admin/webroot/js/modal/modal-product-name-edit.js
index 5ed0d69401..74c57e1be8 100644
--- a/plugins/Admin/webroot/js/modal/modal-product-name-edit.js
+++ b/plugins/Admin/webroot/js/modal/modal-product-name-edit.js
@@ -39,8 +39,8 @@ foodcoopshop.ModalProductNameEdit = {
if ($('.storage-location-dropdown-wrapper').length > 0) {
html += ' ';
html += '';
- html += '' + foodcoopshop.LocalizedJs.dialogProduct.StorageLocation + ' ';
- html += ' ';
+ html += '' + foodcoopshop.LocalizedJs.dialogProduct.StorageLocation + ' ';
+ html += ' ';
html += '
';
}
diff --git a/plugins/Admin/webroot/js/modal/modal-product-status-edit-bulk.js b/plugins/Admin/webroot/js/modal/modal-product-status-edit-bulk.js
new file mode 100644
index 0000000000..50d84f395f
--- /dev/null
+++ b/plugins/Admin/webroot/js/modal/modal-product-status-edit-bulk.js
@@ -0,0 +1,113 @@
+/**
+ * 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.ModalProductStatusEditBulk = {
+
+ init : function() {
+
+ var modalSelector = '#modal-product-status-edit-bulk';
+
+ var button = $('#editStatusForSelectedProducts');
+ foodcoopshop.Helper.disableButton(button);
+
+ $('table.list').find('input.row-marker[type="checkbox"],#row-marker-all').on('click', function () {
+ foodcoopshop.Admin.updateObjectSelectionActionButton(button);
+ });
+
+ button.on('click', function () {
+
+ var productIds = foodcoopshop.Admin.getSelectedProductIds();
+
+ var html = '';
+ var infoText = '';
+
+ if (productIds.length == 1) {
+ infoText = foodcoopshop.LocalizedJs.admin.YouSelectedOneProduct;
+ } else {
+ infoText = foodcoopshop.LocalizedJs.admin.YouSelected0Products.replace(/\{0\}/, '' + productIds.length + ' ');
+ }
+ infoText += ' ';
+
+ var products = [];
+ for (var i in productIds) {
+ products.push($('tr#product-' + productIds[i] + ' span.product-name').html());
+ }
+ html += '' + products.join(' ') + ' ';
+
+ var buttons = [
+ foodcoopshop.Modal.createButton(['btn-success'], foodcoopshop.LocalizedJs.admin.Activate, 'fa-fw fas fa-check'),
+ foodcoopshop.Modal.createButton(['btn-danger'], foodcoopshop.LocalizedJs.admin.Deactivate, 'fa-fw fas fa-minus-circle'),
+ foodcoopshop.Modal.createButton(['btn-outline-light'], foodcoopshop.LocalizedJs.helper.cancel, null, true)
+ ];
+
+ foodcoopshop.Modal.appendModalToDom(
+ modalSelector,
+ foodcoopshop.LocalizedJs.admin.ChangeStatus,
+ html,
+ buttons
+ );
+
+ foodcoopshop.Modal.bindSuccessButton(modalSelector, function() {
+ foodcoopshop.ModalProductStatusEditBulk.getSuccessHandler(modalSelector, 1);
+ });
+
+ $(modalSelector + ' .modal-footer .btn-danger').on('click', function() {
+ foodcoopshop.Helper.addSpinnerToButton($(this), 'fa-minus-circle');
+ foodcoopshop.Helper.disableButton($(this));
+ foodcoopshop.ModalProductStatusEditBulk.getSuccessHandler(modalSelector, 0);
+ });
+
+ $(modalSelector).on('hidden.bs.modal', function (e) {
+ foodcoopshop.ModalProductStatusEditBulk.getCloseHandler(modalSelector);
+ });
+ foodcoopshop.ModalProductStatusEditBulk.getOpenHandler(modalSelector);
+
+ });
+
+ },
+
+ getCloseHandler : function(modalSelector) {
+ $(modalSelector).remove();
+ },
+
+ getSuccessHandler : function(modalSelector, status) {
+
+ var productIds = foodcoopshop.Admin.getSelectedProductIds();
+
+ foodcoopshop.Helper.ajaxCall(
+ '/admin/products/changeStatusBulk/',
+ {
+ productIds: productIds,
+ status: status,
+ },
+ {
+ onOk: function (data) {
+ document.location.reload();
+ },
+ onError: function (data) {
+ var message = '';
+ message += foodcoopshop.LocalizedJs.admin.ErrorsOccurredWhileProductStatusWasChanged;
+ message += ':
';
+ message = message + data.msg;
+ foodcoopshop.Modal.appendFlashMessage(modalSelector, message);
+ foodcoopshop.Modal.resetButtons(modalSelector);
+ }
+ }
+ );
+ },
+
+ getOpenHandler : function(modalSelector) {
+ new bootstrap.Modal(document.getElementById(modalSelector.replace(/#/, ''))).show();
+ }
+
+};
\ No newline at end of file
diff --git a/plugins/Admin/webroot/js/modal/modal-product-tax-edit.js b/plugins/Admin/webroot/js/modal/modal-product-tax-edit.js
index 1f429683e9..988e9fb908 100644
--- a/plugins/Admin/webroot/js/modal/modal-product-tax-edit.js
+++ b/plugins/Admin/webroot/js/modal/modal-product-tax-edit.js
@@ -85,14 +85,14 @@ foodcoopshop.ModalProductTaxEdit = {
var select = $(modalSelector).find('select[name="Taxes[id_tax]"]');
var newId = select.attr('id') + '-' + productId;
select.attr('id', newId);
- var preselectedTaxRate = $('#product-' + productId + ' input[name="Products[id_tax]"').val();
+ var preselectedTaxRate = $('#product-' + productId + ' input[name="Products[id_tax]"]').val();
select.val(preselectedTaxRate);
select = $(modalSelector).find('select[name="PurchasePriceTaxes[id_tax]"]');
if (select.length > 0) {
newId = select.attr('id') + '-' + productId;
select.attr('id', newId);
- preselectedTaxRate = $('#product-' + productId + ' input[name="PurchasePriceProducts[id_tax]"').val();
+ preselectedTaxRate = $('#product-' + productId + ' input[name="PurchasePriceProducts[id_tax]"]').val();
select.val(preselectedTaxRate);
}
diff --git a/plugins/Admin/webroot/js/upload.js b/plugins/Admin/webroot/js/upload.js
index 0181903942..57574b80b4 100644
--- a/plugins/Admin/webroot/js/upload.js
+++ b/plugins/Admin/webroot/js/upload.js
@@ -27,7 +27,7 @@ foodcoopshop.Upload = {
if (image.length == 0) {
return;
}
- $('body.blog_posts input[name="BlogPosts[tmp_image]"').val(image.attr('src'));
+ $('body.blog_posts input[name="BlogPosts[tmp_image]"]').val(image.attr('src'));
var button = $('body.blog_posts a.add-image-button');
button.removeClass('uploaded').addClass('uploaded');
button.html('');
@@ -41,7 +41,7 @@ foodcoopshop.Upload = {
if (image.length == 0) {
return;
}
- $('body.manufacturers input[name="Manufacturers[tmp_image]"').val(image.attr('src'));
+ $('body.manufacturers input[name="Manufacturers[tmp_image]"]').val(image.attr('src'));
var button = $('body.manufacturers a.add-image-button');
button.removeClass('uploaded').addClass('uploaded');
button.html('');
@@ -55,7 +55,7 @@ foodcoopshop.Upload = {
if (image.length == 0) {
return;
}
- $('body.customers input[name="Customers[tmp_image]"').val(image.attr('src'));
+ $('body.customers input[name="Customers[tmp_image]"]').val(image.attr('src'));
var button = $('body.customers a.add-image-button');
button.removeClass('uploaded').addClass('uploaded');
button.html('');
@@ -66,7 +66,7 @@ foodcoopshop.Upload = {
saveManufacturerTmpGeneralTermsAndConditionsInForm : function(modalSelector) {
var filename = $(modalSelector + ' form .drop a').attr('href');
- $('body.manufacturers input[name="Manufacturers[tmp_general_terms_and_conditions]"').val(filename);
+ $('body.manufacturers input[name="Manufacturers[tmp_general_terms_and_conditions]"]').val(filename);
var button= $('body.manufacturers a.add-general-terms-and-conditions-button');
button.removeClass('uploaded').addClass('uploaded').find('a').attr('href', filename);
button.find('span').text(foodcoopshop.LocalizedJs.upload.ChangeGeneralTermsAndConditions);
@@ -78,7 +78,7 @@ foodcoopshop.Upload = {
if (image.length == 0) {
return;
}
- $('body.categories input[name="Categories[tmp_image]"').val(image.attr('src'));
+ $('body.categories input[name="Categories[tmp_image]"]').val(image.attr('src'));
var button = $('body.categories a.add-image-button');
button.removeClass('uploaded').addClass('uploaded');
button.html('');
@@ -92,7 +92,7 @@ foodcoopshop.Upload = {
if (image.length == 0) {
return;
}
- $('body.sliders input[name="Sliders[tmp_image]"').val(image.attr('src'));
+ $('body.sliders input[name="Sliders[tmp_image]"]').val(image.attr('src'));
var button = $('body.sliders a.add-image-button');
button.removeClass('uploaded').addClass('uploaded');
button.html('');
diff --git a/plugins/Network/config/routes.php b/plugins/Network/config/routes.php
index 025d358cde..92f224557b 100644
--- a/plugins/Network/config/routes.php
+++ b/plugins/Network/config/routes.php
@@ -1,4 +1,6 @@
set([
'app' => [
'name' => $this->getInstallationName(),
- 'domain' => Configure::read('app.cakeServerName')
+ 'domain' => Configure::read('App.fullBaseUrl')
],
'status' => count($syncFieldsError) == 0,
'msg' => $message,
@@ -399,7 +401,7 @@ public function updateProducts()
private function getInstallationName()
{
- return Configure::check('appDb.FCS_APP_NAME') ? Configure::read('appDb.FCS_APP_NAME') : Configure::read('app.cakeServerName');
+ return Configure::check('appDb.FCS_APP_NAME') ? Configure::read('appDb.FCS_APP_NAME') : Configure::read('App.fullBaseUrl');
}
public function getProducts()
@@ -416,7 +418,7 @@ public function getProducts()
$this->set([
'app' => [
'name' => $this->getInstallationName(),
- 'domain' => Configure::read('app.cakeServerName'),
+ 'domain' => Configure::read('App.fullBaseUrl'),
'variableMemberFee' => $variableMemberFee
],
'loggedUser' => $this->AppAuth->user(),
@@ -424,4 +426,70 @@ public function getProducts()
]);
$this->viewBuilder()->setOption('serialize', ['app', 'loggedUser', 'products']);
}
+
+ public function getOrders()
+ {
+
+ $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())));
+
+ $pickupDay = h($this->getRequest()->getQuery('pickupDay'));
+ $formattedPickupDay = Configure::read('app.timeHelper')->formatToDbFormatDate($pickupDay);
+
+ if (empty($pickupDay) || $formattedPickupDay == '1970-01-01') {
+ $this->set([
+ 'error' => 'wrong pickupDay format',
+ ]);
+ $this->viewBuilder()->setOption('serialize', ['error']);
+ return;
+ }
+ $this->OrderDetail = $this->getTableLocator()->get('OrderDetails');
+ $conditions = [
+ 'Products.id_manufacturer' => $this->AppAuth->getManufacturerId(),
+ ];
+ $exp = new QueryExpression();
+ $conditions[] = $exp->eq('DATE_FORMAT(OrderDetails.pickup_day, \'%Y-%m-%d\')', $formattedPickupDay);
+
+ $orderDetails = $this->OrderDetail->find('all', [
+ 'conditions' => $conditions,
+ 'contain' => [
+ 'Products',
+ 'OrderDetailUnits',
+ ],
+ ]);
+
+ $preparedOrders = [];
+ foreach($orderDetails as $orderDetail)
+ {
+ $preparedOrder = [
+ 'id' => $orderDetail->id_order_detail,
+ 'product_id' => $orderDetail->product_id,
+ 'attribute_id' => $orderDetail->product_attribute_id,
+ 'name' => $orderDetail->product_name,
+ 'amount' => $orderDetail->product_amount,
+ 'order_state' => $orderDetail->order_state,
+ 'created' => $orderDetail->created,
+ ];
+ if (!empty($orderDetail->order_detail_unit)) {
+ $preparedOrder['unit'] = [
+ 'name' => $orderDetail->order_detail_unit->unit_name,
+ 'product_quantity_in_units' => $orderDetail->order_detail_unit->product_quantity_in_units,
+ 'mark_as_saved' => (bool) $orderDetail->order_detail_unit->mark_as_saved,
+ ];
+ }
+ $preparedOrders[] = $preparedOrder;
+ }
+
+ $this->set([
+ 'app' => [
+ 'name' => $this->getInstallationName(),
+ 'domain' => Configure::read('App.fullBaseUrl'),
+ 'orders' => $preparedOrders,
+ ],
+ ]);
+ $this->viewBuilder()->setOption('serialize', ['app', 'orders']);
+
+ }
+
}
diff --git a/plugins/Network/src/Controller/SyncDomainsController.php b/plugins/Network/src/Controller/SyncDomainsController.php
index 66e713080c..bc91723bf1 100644
--- a/plugins/Network/src/Controller/SyncDomainsController.php
+++ b/plugins/Network/src/Controller/SyncDomainsController.php
@@ -1,4 +1,5 @@
$matchedProducts,
'app' => [
'name' => Configure::read('appDb.FCS_APP_NAME'),
- 'domain' => Configure::read('app.cakeServerName')
+ 'domain' => Configure::read('App.fullBaseUrl')
]
];
$localResponse = json_decode(json_encode($localResponse), true);
diff --git a/plugins/Network/src/Model/Table/SyncDomainsTable.php b/plugins/Network/src/Model/Table/SyncDomainsTable.php
index 645ba14b73..8fc0350404 100644
--- a/plugins/Network/src/Model/Table/SyncDomainsTable.php
+++ b/plugins/Network/src/Model/Table/SyncDomainsTable.php
@@ -1,4 +1,5 @@
getDbFormattedPickupDayByDbFormattedDate(date('Y-m-d')),
- json_encode(Configure::read('app.cakeServerName')),
+ DeliveryRhythm::getDbFormattedPickupDayByDbFormattedDate(date('Y-m-d')),
+ json_encode(Configure::read('App.fullBaseUrl')),
],
[
'2020-01-17',
'"{{serverName}}"',
],
- $this->_response
+ $this->_response->getBody()->__toString(),
);
$this->assertSameAsFile('products-for-demo-vegetable-manufacturer.json', $preparedResponse);
}
-}
+ public function testGetOrdersWrongPickupDayFormat()
+ {
+ $this->configRequest([
+ 'environment' => [
+ 'PHP_AUTH_USER' => Configure::read('test.loginEmailMeatManufacturer'),
+ 'PHP_AUTH_PW' => Configure::read('test.loginPassword'),
+ ]
+ ]);
+ $this->get('/api/getOrders.json?pickupDay=test');
+ $response = json_decode($this->_response->getBody()->__toString());
+ $this->assertEquals('wrong pickupDay format', $response->error);
+ }
+
+ public function testGetOrdersOk()
+ {
+
+ $this->loginAsSuperadmin();
+ $productIdA = 347; // forelle
+ $productIdB = '348-11'; // rindfleisch, 0,5 kg
+ $productIdC = '103'; // bratwürstel
+ $this->addProductToCart($productIdA, 2);
+ $this->addProductToCart($productIdB, 3);
+ $this->addProductToCart($productIdC, 1);
+ $this->finishCart(1, 1);
+
+ $productsTable = $this->getTableLocator()->get('Products');
+ $dummyProduct = $productsTable->newEntity([
+ 'delivery_rhythm_type' => 'week',
+ 'delivery_rhythm_count' => '1',
+ 'is_stock_product' => '0',
+ ]);
+ $nextDeliveryDay = DeliveryRhythm::getNextPickupDayForProduct($dummyProduct);
+
+ $this->configRequest([
+ 'environment' => [
+ 'PHP_AUTH_USER' => Configure::read('test.loginEmailMeatManufacturer'),
+ 'PHP_AUTH_PW' => Configure::read('test.loginPassword'),
+ ]
+ ]);
+ $this->get('/api/getOrders.json?pickupDay=' . $nextDeliveryDay);
+ $response = json_decode($this->_response->getBody()->__toString());
+
+ $this->assertEquals(4, $response->app->orders[0]->id);
+ $this->assertEquals(348, $response->app->orders[0]->product_id);
+ $this->assertEquals(11, $response->app->orders[0]->attribute_id);
+ $this->assertEquals('Rindfleisch', $response->app->orders[0]->name);
+ $this->assertEquals(3, $response->app->orders[0]->amount);
+ $this->assertEquals(3, $response->app->orders[0]->order_state);
+ $this->assertEquals('kg', $response->app->orders[0]->unit->name);
+ $this->assertEquals(1.500, $response->app->orders[0]->unit->product_quantity_in_units);
+ $this->assertEquals(false, $response->app->orders[0]->unit->mark_as_saved);
+
+ $this->assertEquals(5, $response->app->orders[1]->id);
+ $this->assertEquals(347, $response->app->orders[1]->product_id);
+ $this->assertEquals(0, $response->app->orders[1]->attribute_id);
+ $this->assertEquals('Forelle : Stück', $response->app->orders[1]->name);
+ $this->assertEquals(2, $response->app->orders[1]->amount);
+ $this->assertEquals(3, $response->app->orders[1]->order_state);
+ $this->assertEquals('g', $response->app->orders[1]->unit->name);
+ $this->assertEquals(700, $response->app->orders[1]->unit->product_quantity_in_units);
+ $this->assertEquals(false, $response->app->orders[1]->unit->mark_as_saved);
+
+ $this->assertEquals(6, $response->app->orders[2]->id);
+ $this->assertEquals(103, $response->app->orders[2]->product_id);
+ $this->assertEquals(0, $response->app->orders[2]->attribute_id);
+ $this->assertEquals('Bratwürstel', $response->app->orders[2]->name);
+ $this->assertEquals(1, $response->app->orders[2]->amount);
+ $this->assertEquals(3, $response->app->orders[2]->order_state);
+ $this->assertFalse(isset($response->app->orders[2]->unit));
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/Network/tests/TestCase/src/Controller/SyncDomainsControllerTest.php b/plugins/Network/tests/TestCase/src/Controller/SyncDomainsControllerTest.php
index b3901948d6..fa4504c052 100644
--- a/plugins/Network/tests/TestCase/src/Controller/SyncDomainsControllerTest.php
+++ b/plugins/Network/tests/TestCase/src/Controller/SyncDomainsControllerTest.php
@@ -1,4 +1,6 @@
dbConnection->query("UPDATE fcs_sync_domains SET domain = REPLACE(domain, '{{serverName}}', '" . Configure::read('app.cakeServerName') . "');");
+ $this->dbConnection->query("UPDATE fcs_sync_domains SET domain = REPLACE(domain, '{{serverName}}', '" . Configure::read('App.fullBaseUrl') . "');");
}
public function testDenyAccessIfVariableMemberFeeEnabled()
@@ -78,7 +82,7 @@ public function testSaveProductAssociationWithWrongDomain()
$domain = 'http://www.not-available-domain.at';
$response = $this->saveProductRelation(152, 152, '', $domain);
- $this->assertFalse((boolean) $response->status);
+ $this->assertFalse((bool) $response->status);
$this->assertRegExpWithUnquotedString($domain, $response->msg);
$this->assertResponseCode(500);
}
@@ -91,9 +95,9 @@ public function testSaveProductAssociationForProductThatIsNotOwnedByLoggedInManu
$productId = 47; // joghurt, owner: milk manufactuer
$productName = 'Joghurt';
- $response = $this->saveProductRelation($productId, $productId, $productName, Configure::read('app.cakeServerName'));
+ $response = $this->saveProductRelation($productId, $productId, $productName, Configure::read('App.fullBaseUrl'));
- $this->assertFalse((boolean) $response->status);
+ $this->assertFalse((bool) $response->status);
$this->assertRegExpWithUnquotedString('product ' . $productId . ' is not associated with manufacturer ' . $manufacturerId, $response->msg);
$this->assertResponseCode(500);
}
@@ -105,12 +109,12 @@ public function testCorrectSaveProductAssociation()
$productId = 339;
$productName = 'Kartoffel';
- $response = $this->saveProductRelation($productId, $productId, $productName, Configure::read('app.cakeServerName'));
+ $response = $this->saveProductRelation($productId, $productId, $productName, Configure::read('App.fullBaseUrl'));
$this->assertTrue($response->status);
$this->assertNotEmpty($response->product);
$this->assertEquals($response->product->localProductId, $productId);
$this->assertEquals($response->product->remoteProductId, $productId);
- $this->assertEquals($response->product->domain, Configure::read('app.cakeServerName'));
+ $this->assertEquals($response->product->domain, Configure::read('App.fullBaseUrl'));
$this->assertEquals($response->product->productName, strip_tags($productName, ''));
$this->assertResponseOk();
}
@@ -122,7 +126,7 @@ public function testCorrectDeleteProductAssociation()
$productId = 339;
$productName = 'Kartoffel';
- $this->saveProductRelation($productId, $productId, $productName, Configure::read('app.cakeServerName'));
+ $this->saveProductRelation($productId, $productId, $productName, Configure::read('App.fullBaseUrl'));
$response = $this->deleteProductRelation($productId, $productId, $productName);
$this->assertTrue($response->status);
@@ -145,7 +149,7 @@ private function deleteProductRelation($localProductId, $remoteProductId, $produ
[
'localProductId' => $localProductId,
'remoteProductId' => $remoteProductId,
- 'domain' => Configure::read('app.cakeServerName'),
+ 'domain' => Configure::read('App.fullBaseUrl'),
'productName' => $productName
]
]);
@@ -172,7 +176,7 @@ private function saveProductRelation($localProductId, $remoteProductId, $product
private function disableVariableMemberFee()
{
- $this->changeReadOnlyConfiguration('FCS_USE_VARIABLE_MEMBER_FEE', 0);
+ $this->changeConfiguration('FCS_USE_VARIABLE_MEMBER_FEE', 0);
$manufacturerId = $this->Customer->getManufacturerIdByCustomerId(Configure::read('test.vegetableManufacturerId'));
$this->changeManufacturer($manufacturerId, 'variable_member_fee', 0);
}
diff --git a/plugins/Network/tests/comparisons/products-for-demo-vegetable-manufacturer.json b/plugins/Network/tests/comparisons/products-for-demo-vegetable-manufacturer.json
index cb918d9971..af74a65d1a 100644
--- a/plugins/Network/tests/comparisons/products-for-demo-vegetable-manufacturer.json
+++ b/plugins/Network/tests/comparisons/products-for-demo-vegetable-manufacturer.json
@@ -100,7 +100,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": null,
"tax": {
@@ -218,7 +219,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": null,
"tax": {
@@ -328,7 +330,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": null,
"tax": {
@@ -438,7 +441,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": {
"id": 4,
@@ -571,7 +575,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": {
"id": 8,
@@ -825,7 +830,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": null,
"tax": {
@@ -1068,7 +1074,8 @@
"send_product_sold_out_limit_reached_for_contact_person": 1,
"no_delivery_days": "",
"include_stock_products_in_order_lists": 1,
- "send_delivery_notes": 0
+ "send_delivery_notes": 0,
+ "anonymize_customers": 0
},
"unit_product": null,
"tax": {
diff --git a/plugins/Network/webroot/css/sync.css b/plugins/Network/webroot/css/sync.css
index f235bb4d50..675a3b6a9e 100644
--- a/plugins/Network/webroot/css/sync.css
+++ b/plugins/Network/webroot/css/sync.css
@@ -114,6 +114,10 @@ body.syncs.product div.product-list table.list th, body.syncs.product div.produc
border-right: none;
}
+body.syncs div.product-list table.list th {
+ /* override sticky, that would need some extra work */
+ position: inherit;
+}
body.syncs.product div.product-list>b {
margin-top: 5px;
}
diff --git a/plugins/Network/webroot/js/sync-base.js b/plugins/Network/webroot/js/sync-base.js
index 10dc0db7ee..3a78e11ff3 100644
--- a/plugins/Network/webroot/js/sync-base.js
+++ b/plugins/Network/webroot/js/sync-base.js
@@ -16,6 +16,7 @@ foodcoopshop.SyncBase = {
activeAjaxRequests : [],
init : function () {
+ foodcoopshop.ColorMode.init();
foodcoopshop.Helper.showContent();
foodcoopshop.Helper.initMenu();
foodcoopshop.ModalLogout.init();
@@ -205,9 +206,6 @@ foodcoopshop.SyncBase = {
}
}
foodcoopshop.Helper.showOrAppendErrorMessage(syncDomain + ': ' + errorMessage);
- $('.ui-dialog-content').dialog('close');
- $('.ui-dialog .ajax-loader').hide();
- $('.ui-dialog button').attr('disabled', false);
// TODO better implementation needed - maybe add callback for failures?
$('.product-list').removeClass('loader');
});
diff --git a/resources/locales/admin.pot b/resources/locales/admin.pot
deleted file mode 100644
index 50b07d6ea8..0000000000
--- a/resources/locales/admin.pot
+++ /dev/null
@@ -1,25 +0,0 @@
-# LANGUAGE translation of CakePHP Application
-# Copyright YEAR NAME
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PROJECT VERSION\n"
-"POT-Creation-Date: 2022-08-18 18:16+0200\n"
-"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
-"Last-Translator: NAME \n"
-"Language-Team: LANGUAGE \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-
-msgid "Filename_Terms-of-use"
-msgstr ""
-
-msgid "Purchase_price"
-msgstr ""
-
-msgid "Deposit"
-msgstr ""
-
diff --git a/resources/locales/de_DE/cake.mo b/resources/locales/de_DE/cake.mo
index 108901fdcf..595b4cf746 100644
Binary files a/resources/locales/de_DE/cake.mo and b/resources/locales/de_DE/cake.mo differ
diff --git a/resources/locales/de_DE/cake.po b/resources/locales/de_DE/cake.po
index a5502801e0..1f4cfe55f6 100644
--- a/resources/locales/de_DE/cake.po
+++ b/resources/locales/de_DE/cake.po
@@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"POT-Creation-Date: 2018-06-11 18:16+0200\n"
-"PO-Revision-Date: 2021-01-27 14:37+0100\n"
+"PO-Revision-Date: 2022-12-21 16:47+0100\n"
"Last-Translator: \n"
"Language-Team: EMAIL@ADDRESS\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 2.4.2\n"
+"X-Generator: Poedit 3.2.2\n"
msgid "You are not authorized to access that location."
msgstr ""
@@ -28,7 +28,7 @@ msgid "Not Found"
msgstr "Seite nicht gefunden."
msgid "An Internal Error Has Occurred."
-msgstr "Oooohhh, es ist ein Fehler aufgetreten."
+msgstr "Es ist ein Fehler aufgetreten."
msgid "The requested file contains `..` and will not be read."
msgstr ""
diff --git a/resources/locales/de_DE/default.mo b/resources/locales/de_DE/default.mo
index 566850c157..93b9cbd7e0 100644
Binary files a/resources/locales/de_DE/default.mo and b/resources/locales/de_DE/default.mo differ
diff --git a/resources/locales/de_DE/default.po b/resources/locales/de_DE/default.po
index c6b7e7bfd6..79f4b7220e 100644
--- a/resources/locales/de_DE/default.po
+++ b/resources/locales/de_DE/default.po
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
-"POT-Creation-Date: 2022-08-30 14:09+0200\n"
-"PO-Revision-Date: 2022-08-18 18:21+0200\n"
+"POT-Creation-Date: 2023-01-23 17:42+0100\n"
+"PO-Revision-Date: 2023-02-06 08:27+0100\n"
"Last-Translator: \n"
"Language-Team: \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 "Access_denied_please_sign_in."
msgstr "Zugriff verweigert, bitte melde dich an."
@@ -111,6 +111,75 @@ msgstr "abgeschlossen"
msgid "route_self_service"
msgstr "selbstbedienung"
+msgid "Runtime"
+msgstr "Laufzeit"
+
+msgid "Database_backup_successful"
+msgstr "Datenbank-Backup erfolgreich"
+
+msgid "Please_add_credit"
+msgstr "Bitte lade dein Guthaben auf"
+
+msgid "Sum"
+msgstr "Summe"
+
+msgid "Sent_emails"
+msgstr "Versendete E-Mails"
+
+msgid "Order_reminder"
+msgstr "Bestell-Erinnerung"
+
+msgid "today"
+msgstr "heute"
+
+msgid "tomorrow"
+msgstr "morgen"
+
+msgid "Pickup_reminder_for"
+msgstr "Abhol-Erinnerung für"
+
+msgid "{0,plural,=1{1_delivery_note_was} other{#_delivery_notes_were}}_generated_successfully."
+msgstr "{0,plural,=1{1 Lieferschein wurde} other{# Lieferscheine wurden}} erfolgreich erstellt."
+
+msgid "Delivery_note_for_{0}"
+msgstr "Lieferschein für {0}"
+
+msgid "{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully."
+msgstr "{0,plural,=1{1 Rechnung wurde} other{# Rechnungen wurden}} erfolgreich erstellt."
+
+msgid "to_(time_context)"
+msgstr "bis"
+
+msgid "Generated_invoices"
+msgstr "Generierte Rechnungen"
+
+msgid "Invoices_for_{0}_have_been_sent"
+msgstr "Rechnungen für {0} wurden verschickt"
+
+msgid "{0,plural,=1{1_product} other{#_products}}"
+msgstr "{0,plural,=1{1 Produkt} other{# Produkte}}"
+
+msgid "Manufacturer"
+msgstr "Hersteller"
+
+msgid "Invoice_number_abbreviation"
+msgstr "Re. Nr."
+
+msgid "Sent"
+msgstr "Versendet"
+
+msgid "Products"
+msgstr "Produkte"
+
+msgid "Total_sum"
+msgstr "Gesamtsumme"
+
+msgid "Sent_order_lists"
+msgstr "Verschickte Bestelllisten"
+
+msgid "Delivery_day"
+msgstr "Liefertag"
+
msgid "You_have_been_signed_out."
msgstr "Du hast dich erfolgreich abgemeldet."
@@ -162,6 +231,36 @@ msgstr "Neue Produkte"
msgid "Search"
msgstr "Suche"
+msgid "Your_order_has_been_placed_succesfully."
+msgstr "Deine Bestellung wurde erfolgreich abgeschlossen."
+
+msgid "{0}_has_placed_a_new_order_({1})."
+msgstr "{0} hat eine neue Bestellung getätigt ({1})."
+
+msgid "Instant_order_({0})_successfully_placed_for_{1}."
+msgstr "Die Sofort-Bestellung ({0}) für {1} wurde erfolgreich getätigt."
+
+msgid "Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}"
+msgstr "Die Sofort-Bestellung ({0}) für {1} wurde erfolgreich getätigt. Folgende Hersteller wurden darüber informiert: {2}"
+
+msgid "Pickup_day"
+msgstr "Abholtag"
+
+msgid "Thank_you_for_your_purchase!"
+msgstr "Vielen Dank für deinen Einkauf!"
+
+msgid "Sign_out"
+msgstr "Abmelden"
+
+msgid "Continue_shopping"
+msgstr "Weiter einkaufen"
+
+msgid "Print_receipt"
+msgstr "Beleg drucken"
+
+msgid "{0}_has_placed_a_new_order_for_{1}_({2})."
+msgstr "{0} hat eine neue Bestellung für {1} getätigt ({2})."
+
msgid "Please_add_credit_({0})_(minimal_credit_is_{1})."
msgstr "Bitte lade neues Guthaben auf. Dein Guthaben abzüglich Warenkorb beträgt {0}, du kannst bis {1} bestellen."
@@ -210,36 +309,6 @@ msgstr "{0} hat die Lieferpause aktiviert und das Produkt {1} ist nicht mehr bes
msgid "Errors_occurred."
msgstr "Es sind Fehler aufgetreten."
-msgid "Your_order_has_been_placed_succesfully."
-msgstr "Deine Bestellung wurde erfolgreich abgeschlossen."
-
-msgid "{0}_has_placed_a_new_order_({1})."
-msgstr "{0} hat eine neue Bestellung getätigt ({1})."
-
-msgid "Instant_order_({0})_successfully_placed_for_{1}."
-msgstr "Die Sofort-Bestellung ({0}) für {1} wurde erfolgreich getätigt."
-
-msgid "Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}"
-msgstr "Die Sofort-Bestellung ({0}) für {1} wurde erfolgreich getätigt. Folgende Hersteller wurden darüber informiert: {2}"
-
-msgid "Pickup_day"
-msgstr "Abholtag"
-
-msgid "Thank_you_for_your_purchase!"
-msgstr "Vielen Dank für deinen Einkauf!"
-
-msgid "Sign_out"
-msgstr "Abmelden"
-
-msgid "Continue_shopping"
-msgstr "Weiter einkaufen"
-
-msgid "Print_receipt"
-msgstr "Beleg drucken"
-
-msgid "{0}_has_placed_a_new_order_for_{1}_({2})."
-msgstr "{0} hat eine neue Bestellung für {1} getätigt ({2})."
-
msgid "Notification_about_instant_order_order"
msgstr "Benachrichtigung über Sofort-Bestellung"
@@ -255,6 +324,9 @@ msgstr "Du kannst diese E-Mail in den Hersteller-Einstellungen a
msgid "Your_purchase"
msgstr "Dein Einkauf"
+msgid "New_order_comment__was_written_by_{0}_for_{1}"
+msgstr "Neuer Bestell-Kommentar von {0} für {1}"
+
msgid "Order_confirmation"
msgstr "Bestellbestätigung"
@@ -279,6 +351,9 @@ msgstr "Deine E-Mail-Adresse wurde bereits aktiviert oder der Aktivierungscode w
msgid "Your_email_address_has_been_activated_successfully."
msgstr "Deine E-Mail-Adresse wurde erfolgreich aktiviert."
+msgid "Filename_Terms-of-use"
+msgstr "Nutzungsbedingungen"
+
msgid "Your_email_address_has_been_activated_successfully._Your_password_has_been_sent_to_you."
msgstr "Deine E-Mail-Adresse wurde erfolgreich aktiviert, dein Passwort wurde dir soeben zugeschickt."
@@ -624,12 +699,15 @@ msgstr "Grundpreis"
msgid "Price_is_automatically_adapted_after_save."
msgstr "Der Preis wird nach dem Speichern automatisch angepasst."
-msgid "The_field_is_red_if_weight_not_yet_adapted."
-msgstr "Das Feld ist rot, wenn das Gewicht noch nicht angepasst wurde."
-
msgid "Do_not_automatically_apapt_price_just_change_weight."
msgstr "Den Preis nicht automatisch anpassen, nur das Gewicht ändern."
+msgid "Calculator"
+msgstr "Taschenrechner"
+
+msgid "Example_given_abbr"
+msgstr "Z.B."
+
msgid "Really_delete_ordered_product?"
msgstr "Bestelltes Produkt wirklich stornieren?"
@@ -654,17 +732,11 @@ msgstr "Ja, stornieren!"
msgid "Please_only_cancel_if_ok_for_manufacturer!"
msgstr "Nur stornieren, wenn es mit dem Hersteller abgesprochen ist!"
-msgid "Really_delete_selected_product?"
-msgstr "Ausgewähltes Produkt wirklich stornieren?"
-
-msgid "Really_delete_selected_products?"
-msgstr "Ausgewählte Produkte wirklich stornieren?"
+msgid "You_selected_1_product."
+msgstr "Du hast 1 Produkt markiert."
-msgid "You_selected_1_product"
-msgstr "Du hast 1 Produkt ausgewählt"
-
-msgid "You_selected_{0}_products"
-msgstr "Du hast {0} Produkte ausgewählt"
+msgid "You_selected_{0}_products."
+msgstr "Du hast {0} Produkte markiert."
msgid "Adapt_amount_reason_is_mandatory."
msgstr "Bitte gib an, warum die Anzahl vermindert wird."
@@ -720,6 +792,12 @@ msgstr "Produkt {0} wirklich als \"neu\" anzeigen?"
msgid "Really_do_not_show_product_{0}_as_new?"
msgstr "Produkt {0} wirklich als \"nicht neu\" anzeigen?"
+msgid "Activate"
+msgstr "Aktivieren"
+
+msgid "Deactivate"
+msgstr "Deaktivieren"
+
msgid "Activate_product"
msgstr "Produkt aktivieren"
@@ -792,6 +870,9 @@ msgstr "Achtung: Es gibt keine Möglichkeit , das rückgängig zu machen!"
msgid "Errors_occurred_while_member_was_deleted"
msgstr "Beim Löschen des Mitgliedskontos sind Fehler aufgetreten"
+msgid "Errors_occurred_while_product_status_was_changed"
+msgstr "Beim Ändern des Produkt-Status sind Fehler aufgetreten"
+
msgid "Delete_products?"
msgstr "Produkte löschen?"
@@ -901,10 +982,10 @@ msgid "Add_product_feedback"
msgstr "Feedback zum Produkt schreiben"
msgid "Add_product_feedback_explanation_text_{0}."
-msgstr "Dein Feedback wird dem Hersteller {0} nach dem Speichern automatisch per E-Mail geschickt, es ist dann nicht mehr änderbar . Der Hersteller erhält deine E-Mail-Adresse, damit er dir ggfs. zurückschreiben kann."
+msgstr "Dein Feedback wird dem Hersteller {0} nach dem Speichern automatisch per E-Mail geschickt, es ist dann nicht mehr änderbar . Der Hersteller erhält deinen Namen und auch deine E-Mail-Adresse, damit er dir ggfs. zurückschreiben kann."
msgid "Tip:_Change_delivery_rhythm_for_multiple_products:_Select_checkboxes_and_click_bottom_button."
-msgstr "Du kannst den Lieferrhythmus von mehreren Produkten gleichzeitig ändern, indem du sie links auswählst und auf den Button ganz unten klickst."
+msgstr "Du kannst den Lieferrhythmus von mehreren Produkten gleichzeitig ändern, indem du sie links markierst und dann auf den Button ganz unten klickst."
msgid "Generate_invoice"
msgstr "Rechnung erstellen"
@@ -951,6 +1032,12 @@ msgstr "zurück"
msgid "Send_email_to_member"
msgstr "E-Mail an Mitglied versenden"
+msgid "Send_email_to_both_members"
+msgstr "E-Mail an beide Mitglieder versenden"
+
+msgid "Change_status"
+msgstr "Status ändern"
+
msgid "Were_the_products_picked_up?"
msgstr "Wurden die Produkte abgeholt?"
@@ -1047,9 +1134,6 @@ msgstr "Bestellbar bis zu einer Anzahl von"
msgid "zero_or_smaller_zero"
msgstr "0 oder kleiner als 0"
-msgid "Manufacturer"
-msgstr "Hersteller"
-
msgid "For_manufacturers_and_contact_persons._Can_be_changed_in_manufacturer_settings."
msgstr "An Hersteller bzw. Ansprechperson, kann in den Hersteller-Einstellungen geändert werden."
@@ -1071,9 +1155,6 @@ msgstr "Lieferrhythmus ändern"
msgid "First_delivery_day"
msgstr "Erster Liefertag"
-msgid "Delivery_day"
-msgstr "Liefertag"
-
msgid "First_delivery_day_info_(one_product)."
msgstr "Produkt ist ab sofort bestellbar."
@@ -1180,10 +1261,10 @@ msgid "Status"
msgstr "Status"
msgid "No_products_or_attributes_selected."
-msgstr "Es sind keine Produkte oder Varianten ausgewählt."
+msgstr "Es sind keine Produkte oder Varianten markiert."
msgid "No_product_data_selected."
-msgstr "Es sind keine Produktdaten ausgewählt."
+msgstr "Es sind keine Produktdaten markiert."
msgid "Please_enter_your_credentials."
msgstr "Bitte gib deine Login-Daten ein."
@@ -1260,9 +1341,6 @@ msgstr "Rechnung"
msgid "Cancellation_invoice"
msgstr "Storno-Rechnung"
-msgid "Filename_Information-about-right-of-withdrawal"
-msgstr "Informationen-ueber-Ruecktrittsrecht"
-
msgid "ID"
msgstr ""
@@ -1272,9 +1350,6 @@ msgstr "Preis exkl."
msgid "Price_incl."
msgstr "Preis inkl."
-msgid "Total_sum"
-msgstr "Gesamtsumme"
-
msgid "Price_net"
msgstr "Nettopreis"
@@ -1314,9 +1389,36 @@ msgstr "Summe Steuer"
msgid "Sum_price_incl."
msgstr "Summe Preis inkl."
+msgid "Filename_Information-about-right-of-withdrawal"
+msgstr "Informationen-ueber-Ruecktrittsrecht"
+
msgid "You_are_not_signed_in."
msgstr "Du bist nicht angemeldet."
+msgid "BackupDatabaseCronjob"
+msgstr "Datenbank-Backup"
+
+msgid "CheckCreditBalanceCronjob"
+msgstr "Guthaben-Erinnerung versenden"
+
+msgid "EmailOrderReminderCronjob"
+msgstr "Bestell-Erinnerung versenden"
+
+msgid "PickupReminderCronjob"
+msgstr "Abhol-Erinnerung versenden"
+
+msgid "SendOrderListsCronjob"
+msgstr "Bestelllisten versenden"
+
+msgid "SendInvoicesToCustomersCronjob"
+msgstr "Rechnungen versenden"
+
+msgid "SendInvoicesToManufacturersCronjob"
+msgstr "Rechnungen versenden"
+
+msgid "SendDeliveryNotesCronjob"
+msgstr "Lieferscheine versenden"
+
msgid "Action_Log_Invoice_added"
msgstr "Rechnung: erstellt"
@@ -1575,6 +1677,9 @@ msgstr "Feedback freigeschaltet"
msgid "Action_Log_Feedback_deleted"
msgstr "Feedback gelöscht"
+msgid "Action_Log_Cronjob_changed"
+msgstr "Cronjob: geändert"
+
msgid "Action_Log_Cronjob_database_backup_done"
msgstr "Cronjob: Datenbank gebackupt"
@@ -1692,6 +1797,9 @@ msgstr "Das Produkt mit der ID {0} ist nicht vorhanden."
msgid "The_attribute_does_not_exist:_{0}"
msgstr "Die Variante existiert nicht: {0}"
+msgid "Please_accept_the_information_about_right_of_withdrawal"
+msgstr "Bitte akzeptiere die Information über das Rücktrittsrecht und dessen Ausschluss."
+
msgid "Please_accept_the_information_about_right_of_withdrawal."
msgstr "Bitte akzeptiere die Information über das Rücktrittsrecht und dessen Ausschluss."
@@ -1710,6 +1818,9 @@ msgstr "Bitte wähle einen Abholtag aus."
msgid "The_pickup_day_is_not_valid."
msgstr "Der Abholtag ist nicht gültig."
+msgid "Delivery_break"
+msgstr "Lieferpause"
+
msgid "offline"
msgstr "offline"
@@ -1731,6 +1842,45 @@ msgstr "Bitte gib den Namen der Foodcoop an."
msgid "The_amount_of_characters_needs_to_be_between_{0}_and_{1}."
msgstr "Die Anzahl der Zeichen muss zwischen {0} und {1} liegen."
+msgid "The_time_interval_is_not_valid."
+msgstr "Das Intervall ist nicht gültig."
+
+msgid "Please_select_a_day_of_month."
+msgstr "Bitte wähle einen Tag (Monat) aus."
+
+msgid "The_day_of_month_is_not_valid."
+msgstr "Der Tag (Monat) ist nicht gültig."
+
+msgid "Please_select_a_weekday."
+msgstr "Bitte wähle einen Wochentag aus."
+
+msgid "The_weekday_is_not_valid."
+msgstr "Der Wochentag ist nicht gültig."
+
+msgid "daily"
+msgstr "täglich"
+
+msgid "weekly"
+msgstr "wöchentlich"
+
+msgid "No_day_of_month_allowed_for_time_interval_{0}."
+msgstr "Beim Interval \"{0}\" bitte keinen Tag (Monat) angeben."
+
+msgid "monthly"
+msgstr "monatlich"
+
+msgid "No_weekday_allowed_for_time_interval_{0}."
+msgstr "Beim Interval \"{0}\" bitte keinen Wochentag angeben."
+
+msgid "Please_enter_a_valid_time."
+msgstr "Bitte gib eine gültige Uhrzeit ein."
+
+msgid "The_time_interval_needs_to_equal_\"{0}\""
+msgstr "Das Intervall muss \"{0}\" sein."
+
+msgid "Last_day_of_month"
+msgstr "Monatsletzter"
+
msgid "Please_enter_your_first_name."
msgstr "Bitte gib deinen Vornamen an."
@@ -1791,9 +1941,6 @@ msgstr "Bitte gib einen gültigen IBAN ein."
msgid "Please_enter_a_valid_BIC."
msgstr "Bitte gib einen gültigen BIC ein."
-msgid "Delivery_break"
-msgstr "Lieferpause"
-
msgid "online"
msgstr "online"
@@ -1803,9 +1950,6 @@ msgstr "Bitte gib dein Feedback ein."
msgid "Please_enter_a_correct_number."
msgstr "Bitte gib eine korrekte Zahl ein."
-msgid "{0,plural,=1{1_product} other{#_products}}"
-msgstr "{0,plural,=1{1 Produkt} other{# Produkte}}"
-
msgid "The_amount_(money)_needs_to_be_greater_than_0."
msgstr "Der Betrag muss größer als 0 sein."
@@ -1869,6 +2013,9 @@ msgstr "gelöscht"
msgid "The_price_needs_to_be_greater_or_equal_than_0."
msgstr "Der Preis muss eine positive Zahl sein."
+msgid "Purchase_price"
+msgstr "Einkaufspreis"
+
msgid "The_quantity_needs_to_be_a_number."
msgstr "Der Lagerstand muss eine Zahl sein."
@@ -1926,63 +2073,6 @@ msgstr "Mitglied"
msgid "Order_lists_for_the_day"
msgstr "Bestellungen für den"
-msgid "Runtime"
-msgstr "Laufzeit"
-
-msgid "Database_backup_successful"
-msgstr "Datenbank-Backup erfolgreich"
-
-msgid "Please_add_credit"
-msgstr "Bitte lade dein Guthaben auf"
-
-msgid "Sum"
-msgstr "Summe"
-
-msgid "Sent_emails"
-msgstr "Versendete E-Mails"
-
-msgid "Order_reminder"
-msgstr "Bestell-Erinnerung"
-
-msgid "today"
-msgstr "heute"
-
-msgid "tomorrow"
-msgstr "morgen"
-
-msgid "Pickup_reminder_for"
-msgstr "Abhol-Erinnerung für"
-
-msgid "{0,plural,=1{1_delivery_note_was} other{#_delivery_notes_were}}_generated_successfully."
-msgstr "{0,plural,=1{1 Lieferschein wurde} other{# Lieferscheine wurden}} erfolgreich erstellt."
-
-msgid "Delivery_note_for_{0}"
-msgstr "Lieferschein für {0}"
-
-msgid "{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully."
-msgstr "{0,plural,=1{1 Rechnung wurde} other{# Rechnungen wurden}} erfolgreich erstellt."
-
-msgid "to_(time_context)"
-msgstr "bis"
-
-msgid "Generated_invoices"
-msgstr "Generierte Rechnungen"
-
-msgid "Invoices_for_{0}_have_been_sent"
-msgstr "Rechnungen für {0} wurden verschickt"
-
-msgid "Invoice_number_abbreviation"
-msgstr "Re. Nr."
-
-msgid "Sent"
-msgstr "Versendet"
-
-msgid "Products"
-msgstr "Produkte"
-
-msgid "Sent_order_lists"
-msgstr "Verschickte Bestelllisten"
-
msgid "Customer_adds_payment_manually"
msgstr "Mitglieder tragen die Guthaben-Aufladungen selbst ein"
@@ -2025,9 +2115,6 @@ msgstr "Einkaufen zum Einkaufspreis"
msgid "Shopping_with_zero_price"
msgstr "Einkaufen zum Nullpreis"
-msgid "weekly"
-msgstr "wöchentlich"
-
msgid "every_{0}_week"
msgstr "jede {0} Woche"
@@ -2040,6 +2127,12 @@ msgstr "jeden letzten {0} im Monat"
msgid "bulk_order"
msgstr "Sammelbestellung"
+msgid "midnight"
+msgstr "Mitternacht"
+
+msgid "default_value"
+msgstr "Standardwert"
+
msgid "yes"
msgstr "ja"
@@ -2097,6 +2190,15 @@ msgstr "Admin"
msgid "Superadmin"
msgstr "Superadmin"
+msgid "Configurations"
+msgstr "Einstellungen"
+
+msgid "Cronjobs"
+msgstr "Cronjobs"
+
+msgid "Tax_rates"
+msgstr "Steuersätze"
+
msgid "Credit_overview"
msgstr "Guthaben-Übersicht"
@@ -2151,12 +2253,6 @@ msgstr "{0,plural,=1{1 Minute} other{# Minuten}}"
msgid "{0,plural,=1{1_second} other{#_seconds}}"
msgstr "{0,plural,=1{1 Sekunde} other{# Sekunden}}"
-msgid "midnight"
-msgstr "Mitternacht"
-
-msgid "default_value"
-msgstr "Standardwert"
-
msgid "price_will_be_adapted."
msgstr "Preis wird angepasst."
@@ -2400,6 +2496,9 @@ msgstr "Ich verspreche die Produkte am gebuchten Abholtag abzuholen und zahle de
msgid "No_order_possible"
msgstr "Keine Bestellung möglich"
+msgid "{0}_times_ordered_for_pickup_day_{1}."
+msgstr "{0}x für Abholtag {1} bestellt."
+
msgid "available"
msgstr "verfügbar"
@@ -2614,7 +2713,7 @@ msgid "This_email_was_created_automatically."
msgstr "Diese E-Mail wurde automatisch erstellt."
msgid "You_can_unsubscribe_it_ in_your_settings ."
-msgstr "Du kannst sie in deinen Einstellungen abbestellen. ."
+msgstr "Du kannst sie in deinen Einstellungen abbestellen ."
msgid "homepage"
msgstr "Startseite"
@@ -2676,6 +2775,18 @@ msgstr "getätigt am"
msgid "Prices_are_including_vat."
msgstr "Die Preise verstehen sich inklusive Umsatzsteuer."
+#~ msgid "The_field_is_red_if_weight_not_yet_adapted."
+#~ msgstr "Das Feld ist rot, wenn das Gewicht noch nicht angepasst wurde."
+
+#~ msgid "Really_delete_selected_product?"
+#~ msgstr "Markiertes Produkt wirklich stornieren?"
+
+#~ msgid "Really_delete_selected_products?"
+#~ msgstr "Markierte Produkte wirklich stornieren?"
+
+#~ msgid "The_day_of_month_is_required."
+#~ msgstr "Bitte gib einen Tag (Monat) an."
+
#~ msgid "No_stock_products_selected."
#~ msgstr "Es wurden keine Lagerprodukte ausgewählt."
@@ -2718,9 +2829,6 @@ msgstr "Die Preise verstehen sich inklusive Umsatzsteuer."
#~ msgid "Mouseover_shows_the_paid_amont_in_{0}."
#~ msgstr "Mouseover zeigt den bezahlten Betrag in {0} an."
-#~ msgid "Order_contains_amount_in_{0}."
-#~ msgstr "Bestellung wurde teilweise in {0} bezahlt."
-
#~ msgid "Paying_with_time_account_name_{0}"
#~ msgstr "{0}konto"
@@ -2796,9 +2904,6 @@ msgstr "Die Preise verstehen sich inklusive Umsatzsteuer."
#~ msgid "Surcharge"
#~ msgstr "Aufschlag"
-#~ msgid "The_discount_is_not_valid."
-#~ msgstr "Der Rabatt ist nicht gültig."
-
#~ msgid "No_discount_normal_selling_prices_are_used"
#~ msgstr "Kein Rabatt - Einkauf zum normalen Verkaufspreis"
@@ -2833,9 +2938,6 @@ msgstr "Die Preise verstehen sich inklusive Umsatzsteuer."
#~ msgid "Your_email_address_was_activated"
#~ msgstr "Deine E-Mail-Adresse wurde erfolgreich aktiviert und du bist bereits eingeloggt."
-#~ msgid "Filename_Terms-of-use"
-#~ msgstr "Nutzungsbedingungen"
-
#~ msgid "Generate_orders_as_pdf?"
#~ msgstr "Bestellungen als PDF generieren?"
diff --git a/resources/locales/default.pot b/resources/locales/default.pot
index 93bf36d48c..6f07be807e 100644
--- a/resources/locales/default.pot
+++ b/resources/locales/default.pot
@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
-"POT-Creation-Date: 2022-08-30 14:09+0200\n"
+"POT-Creation-Date: 2023-01-23 17:42+0100\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: NAME \n"
"Language-Team: LANGUAGE \n"
@@ -110,6 +110,75 @@ msgstr ""
msgid "route_self_service"
msgstr ""
+msgid "Runtime"
+msgstr ""
+
+msgid "Database_backup_successful"
+msgstr ""
+
+msgid "Please_add_credit"
+msgstr ""
+
+msgid "Sum"
+msgstr ""
+
+msgid "Sent_emails"
+msgstr ""
+
+msgid "Order_reminder"
+msgstr ""
+
+msgid "today"
+msgstr ""
+
+msgid "tomorrow"
+msgstr ""
+
+msgid "Pickup_reminder_for"
+msgstr ""
+
+msgid "{0,plural,=1{1_delivery_note_was} other{#_delivery_notes_were}}_generated_successfully."
+msgstr ""
+
+msgid "Delivery_note_for_{0}"
+msgstr ""
+
+msgid "{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully."
+msgstr ""
+
+msgid "to_(time_context)"
+msgstr ""
+
+msgid "Generated_invoices"
+msgstr ""
+
+msgid "Invoices_for_{0}_have_been_sent"
+msgstr ""
+
+msgid "{0,plural,=1{1_product} other{#_products}}"
+msgstr ""
+
+msgid "Manufacturer"
+msgstr ""
+
+msgid "Invoice_number_abbreviation"
+msgstr ""
+
+msgid "Sent"
+msgstr ""
+
+msgid "Products"
+msgstr ""
+
+msgid "Total_sum"
+msgstr ""
+
+msgid "Sent_order_lists"
+msgstr ""
+
+msgid "Delivery_day"
+msgstr ""
+
msgid "You_have_been_signed_out."
msgstr ""
@@ -161,82 +230,82 @@ msgstr ""
msgid "Search"
msgstr ""
-msgid "Please_add_credit_({0})_(minimal_credit_is_{1})."
+msgid "Your_order_has_been_placed_succesfully."
msgstr ""
-msgid "The_desired_amount_{0}_of_the_product_{1}_is_not_available_any_more_available_amount_{2}."
+msgid "{0}_has_placed_a_new_order_({1})."
msgstr ""
-msgid "Please_change_amount_or_delete_product_from_cart_to_place_order."
+msgid "Instant_order_({0})_successfully_placed_for_{1}."
msgstr ""
-msgid "The_product_{0}_cannot_be_ordered_any_more_due_to_interal_reasons."
+msgid "Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}"
msgstr ""
-msgid "Please_delete_product_from_cart_to_place_order."
+msgid "Pickup_day"
msgstr ""
-msgid "The_desired_amount_{0}_of_the_attribute_{1}_of_the_product_{2}_is_not_available_any_more_available_amount_{3}."
+msgid "Thank_you_for_your_purchase!"
msgstr ""
-msgid "The_attribute_{0}_of_the_product_{1}_cannot_be_ordered_any_more_due_to_interal_reasons."
+msgid "Sign_out"
msgstr ""
-msgid "The_attribute_does_not_exist."
+msgid "Continue_shopping"
msgstr ""
-msgid "The_product_now_contains_attributes."
+msgid "Print_receipt"
msgstr ""
-msgid "You_can_add_it_again_after_having_it_deleted."
+msgid "{0}_has_placed_a_new_order_for_{1}_({2})."
msgstr ""
-msgid "The_product_{0}_is_not_activated_any_more."
+msgid "Please_add_credit_({0})_(minimal_credit_is_{1})."
msgstr ""
-msgid "{0}_can_be_ordered_next_week."
+msgid "The_desired_amount_{0}_of_the_product_{1}_is_not_available_any_more_available_amount_{2}."
msgstr ""
-msgid "The_manufacturer_of_the_product_{0}_has_a_delivery_break_or_product_is_not_activated."
+msgid "Please_change_amount_or_delete_product_from_cart_to_place_order."
msgstr ""
-msgid "It_is_not_possible_to_order_the_product_{0}_any_more."
+msgid "The_product_{0}_cannot_be_ordered_any_more_due_to_interal_reasons."
msgstr ""
-msgid "{0}_has_activated_the_delivery_break_and_product_{1}_cannot_be_ordered."
+msgid "Please_delete_product_from_cart_to_place_order."
msgstr ""
-msgid "Errors_occurred."
+msgid "The_desired_amount_{0}_of_the_attribute_{1}_of_the_product_{2}_is_not_available_any_more_available_amount_{3}."
msgstr ""
-msgid "Your_order_has_been_placed_succesfully."
+msgid "The_attribute_{0}_of_the_product_{1}_cannot_be_ordered_any_more_due_to_interal_reasons."
msgstr ""
-msgid "{0}_has_placed_a_new_order_({1})."
+msgid "The_attribute_does_not_exist."
msgstr ""
-msgid "Instant_order_({0})_successfully_placed_for_{1}."
+msgid "The_product_now_contains_attributes."
msgstr ""
-msgid "Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}"
+msgid "You_can_add_it_again_after_having_it_deleted."
msgstr ""
-msgid "Pickup_day"
+msgid "The_product_{0}_is_not_activated_any_more."
msgstr ""
-msgid "Thank_you_for_your_purchase!"
+msgid "{0}_can_be_ordered_next_week."
msgstr ""
-msgid "Sign_out"
+msgid "The_manufacturer_of_the_product_{0}_has_a_delivery_break_or_product_is_not_activated."
msgstr ""
-msgid "Continue_shopping"
+msgid "It_is_not_possible_to_order_the_product_{0}_any_more."
msgstr ""
-msgid "Print_receipt"
+msgid "{0}_has_activated_the_delivery_break_and_product_{1}_cannot_be_ordered."
msgstr ""
-msgid "{0}_has_placed_a_new_order_for_{1}_({2})."
+msgid "Errors_occurred."
msgstr ""
msgid "Notification_about_instant_order_order"
@@ -254,6 +323,9 @@ msgstr ""
msgid "Your_purchase"
msgstr ""
+msgid "New_order_comment__was_written_by_{0}_for_{1}"
+msgstr ""
+
msgid "Order_confirmation"
msgstr ""
@@ -278,6 +350,9 @@ msgstr ""
msgid "Your_email_address_has_been_activated_successfully."
msgstr ""
+msgid "Filename_Terms-of-use"
+msgstr ""
+
msgid "Your_email_address_has_been_activated_successfully._Your_password_has_been_sent_to_you."
msgstr ""
@@ -623,10 +698,13 @@ msgstr ""
msgid "Price_is_automatically_adapted_after_save."
msgstr ""
-msgid "The_field_is_red_if_weight_not_yet_adapted."
+msgid "Do_not_automatically_apapt_price_just_change_weight."
msgstr ""
-msgid "Do_not_automatically_apapt_price_just_change_weight."
+msgid "Calculator"
+msgstr ""
+
+msgid "Example_given_abbr"
msgstr ""
msgid "Really_delete_ordered_product?"
@@ -653,16 +731,10 @@ msgstr ""
msgid "Please_only_cancel_if_ok_for_manufacturer!"
msgstr ""
-msgid "Really_delete_selected_product?"
+msgid "You_selected_1_product."
msgstr ""
-msgid "Really_delete_selected_products?"
-msgstr ""
-
-msgid "You_selected_1_product"
-msgstr ""
-
-msgid "You_selected_{0}_products"
+msgid "You_selected_{0}_products."
msgstr ""
msgid "Adapt_amount_reason_is_mandatory."
@@ -719,6 +791,12 @@ msgstr ""
msgid "Really_do_not_show_product_{0}_as_new?"
msgstr ""
+msgid "Activate"
+msgstr ""
+
+msgid "Deactivate"
+msgstr ""
+
msgid "Activate_product"
msgstr ""
@@ -791,6 +869,9 @@ msgstr ""
msgid "Errors_occurred_while_member_was_deleted"
msgstr ""
+msgid "Errors_occurred_while_product_status_was_changed"
+msgstr ""
+
msgid "Delete_products?"
msgstr ""
@@ -950,6 +1031,12 @@ msgstr ""
msgid "Send_email_to_member"
msgstr ""
+msgid "Send_email_to_both_members"
+msgstr ""
+
+msgid "Change_status"
+msgstr ""
+
msgid "Were_the_products_picked_up?"
msgstr ""
@@ -1046,9 +1133,6 @@ msgstr ""
msgid "zero_or_smaller_zero"
msgstr ""
-msgid "Manufacturer"
-msgstr ""
-
msgid "For_manufacturers_and_contact_persons._Can_be_changed_in_manufacturer_settings."
msgstr ""
@@ -1070,9 +1154,6 @@ msgstr ""
msgid "First_delivery_day"
msgstr ""
-msgid "Delivery_day"
-msgstr ""
-
msgid "First_delivery_day_info_(one_product)."
msgstr ""
@@ -1259,9 +1340,6 @@ msgstr ""
msgid "Cancellation_invoice"
msgstr ""
-msgid "Filename_Information-about-right-of-withdrawal"
-msgstr ""
-
msgid "ID"
msgstr ""
@@ -1271,9 +1349,6 @@ msgstr ""
msgid "Price_incl."
msgstr ""
-msgid "Total_sum"
-msgstr ""
-
msgid "Price_net"
msgstr ""
@@ -1313,9 +1388,36 @@ msgstr ""
msgid "Sum_price_incl."
msgstr ""
+msgid "Filename_Information-about-right-of-withdrawal"
+msgstr ""
+
msgid "You_are_not_signed_in."
msgstr ""
+msgid "BackupDatabaseCronjob"
+msgstr ""
+
+msgid "CheckCreditBalanceCronjob"
+msgstr ""
+
+msgid "EmailOrderReminderCronjob"
+msgstr ""
+
+msgid "PickupReminderCronjob"
+msgstr ""
+
+msgid "SendOrderListsCronjob"
+msgstr ""
+
+msgid "SendInvoicesToCustomersCronjob"
+msgstr ""
+
+msgid "SendInvoicesToManufacturersCronjob"
+msgstr ""
+
+msgid "SendDeliveryNotesCronjob"
+msgstr ""
+
msgid "Action_Log_Invoice_added"
msgstr ""
@@ -1574,6 +1676,9 @@ msgstr ""
msgid "Action_Log_Feedback_deleted"
msgstr ""
+msgid "Action_Log_Cronjob_changed"
+msgstr ""
+
msgid "Action_Log_Cronjob_database_backup_done"
msgstr ""
@@ -1691,6 +1796,9 @@ msgstr ""
msgid "The_attribute_does_not_exist:_{0}"
msgstr ""
+msgid "Please_accept_the_information_about_right_of_withdrawal"
+msgstr ""
+
msgid "Please_accept_the_information_about_right_of_withdrawal."
msgstr ""
@@ -1709,6 +1817,9 @@ msgstr ""
msgid "The_pickup_day_is_not_valid."
msgstr ""
+msgid "Delivery_break"
+msgstr ""
+
msgid "offline"
msgstr ""
@@ -1730,6 +1841,45 @@ msgstr ""
msgid "The_amount_of_characters_needs_to_be_between_{0}_and_{1}."
msgstr ""
+msgid "The_time_interval_is_not_valid."
+msgstr ""
+
+msgid "Please_select_a_day_of_month."
+msgstr ""
+
+msgid "The_day_of_month_is_not_valid."
+msgstr ""
+
+msgid "Please_select_a_weekday."
+msgstr ""
+
+msgid "The_weekday_is_not_valid."
+msgstr ""
+
+msgid "daily"
+msgstr ""
+
+msgid "weekly"
+msgstr ""
+
+msgid "No_day_of_month_allowed_for_time_interval_{0}."
+msgstr ""
+
+msgid "monthly"
+msgstr ""
+
+msgid "No_weekday_allowed_for_time_interval_{0}."
+msgstr ""
+
+msgid "Please_enter_a_valid_time."
+msgstr ""
+
+msgid "The_time_interval_needs_to_equal_\"{0}\""
+msgstr ""
+
+msgid "Last_day_of_month"
+msgstr ""
+
msgid "Please_enter_your_first_name."
msgstr ""
@@ -1790,9 +1940,6 @@ msgstr ""
msgid "Please_enter_a_valid_BIC."
msgstr ""
-msgid "Delivery_break"
-msgstr ""
-
msgid "online"
msgstr ""
@@ -1802,9 +1949,6 @@ msgstr ""
msgid "Please_enter_a_correct_number."
msgstr ""
-msgid "{0,plural,=1{1_product} other{#_products}}"
-msgstr ""
-
msgid "The_amount_(money)_needs_to_be_greater_than_0."
msgstr ""
@@ -1868,6 +2012,9 @@ msgstr ""
msgid "The_price_needs_to_be_greater_or_equal_than_0."
msgstr ""
+msgid "Purchase_price"
+msgstr ""
+
msgid "The_quantity_needs_to_be_a_number."
msgstr ""
@@ -1925,63 +2072,6 @@ msgstr ""
msgid "Order_lists_for_the_day"
msgstr ""
-msgid "Runtime"
-msgstr ""
-
-msgid "Database_backup_successful"
-msgstr ""
-
-msgid "Please_add_credit"
-msgstr ""
-
-msgid "Sum"
-msgstr ""
-
-msgid "Sent_emails"
-msgstr ""
-
-msgid "Order_reminder"
-msgstr ""
-
-msgid "today"
-msgstr ""
-
-msgid "tomorrow"
-msgstr ""
-
-msgid "Pickup_reminder_for"
-msgstr ""
-
-msgid "{0,plural,=1{1_delivery_note_was} other{#_delivery_notes_were}}_generated_successfully."
-msgstr ""
-
-msgid "Delivery_note_for_{0}"
-msgstr ""
-
-msgid "{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully."
-msgstr ""
-
-msgid "to_(time_context)"
-msgstr ""
-
-msgid "Generated_invoices"
-msgstr ""
-
-msgid "Invoices_for_{0}_have_been_sent"
-msgstr ""
-
-msgid "Invoice_number_abbreviation"
-msgstr ""
-
-msgid "Sent"
-msgstr ""
-
-msgid "Products"
-msgstr ""
-
-msgid "Sent_order_lists"
-msgstr ""
-
msgid "Customer_adds_payment_manually"
msgstr ""
@@ -2024,9 +2114,6 @@ msgstr ""
msgid "Shopping_with_zero_price"
msgstr ""
-msgid "weekly"
-msgstr ""
-
msgid "every_{0}_week"
msgstr ""
@@ -2039,6 +2126,12 @@ msgstr ""
msgid "bulk_order"
msgstr ""
+msgid "midnight"
+msgstr ""
+
+msgid "default_value"
+msgstr ""
+
msgid "yes"
msgstr ""
@@ -2096,6 +2189,15 @@ msgstr ""
msgid "Superadmin"
msgstr ""
+msgid "Configurations"
+msgstr ""
+
+msgid "Cronjobs"
+msgstr ""
+
+msgid "Tax_rates"
+msgstr ""
+
msgid "Credit_overview"
msgstr ""
@@ -2150,12 +2252,6 @@ msgstr ""
msgid "{0,plural,=1{1_second} other{#_seconds}}"
msgstr ""
-msgid "midnight"
-msgstr ""
-
-msgid "default_value"
-msgstr ""
-
msgid "price_will_be_adapted."
msgstr ""
@@ -2399,6 +2495,9 @@ msgstr ""
msgid "No_order_possible"
msgstr ""
+msgid "{0}_times_ordered_for_pickup_day_{1}."
+msgstr ""
+
msgid "available"
msgstr ""
diff --git a/resources/locales/en_US/default.mo b/resources/locales/en_US/default.mo
index 71eb6b1956..9ede64dd5b 100644
Binary files a/resources/locales/en_US/default.mo and b/resources/locales/en_US/default.mo differ
diff --git a/resources/locales/en_US/default.po b/resources/locales/en_US/default.po
index c4d8e1ac99..8cce86ee5d 100644
--- a/resources/locales/en_US/default.po
+++ b/resources/locales/en_US/default.po
@@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
-"POT-Creation-Date: 2022-08-30 14:09+0200\n"
-"PO-Revision-Date: 2022-08-30 14:10+0200\n"
+"POT-Creation-Date: 2023-01-23 17:42+0100\n"
+"PO-Revision-Date: 2023-01-31 15:25+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 "Access_denied_please_sign_in."
msgstr "Access denied, please sign in."
@@ -111,6 +111,75 @@ msgstr "finished"
msgid "route_self_service"
msgstr "self-service"
+msgid "Runtime"
+msgstr "Runtime"
+
+msgid "Database_backup_successful"
+msgstr "Database backup successful"
+
+msgid "Please_add_credit"
+msgstr "Please add credit"
+
+msgid "Sum"
+msgstr "Sum"
+
+msgid "Sent_emails"
+msgstr "Sent emails"
+
+msgid "Order_reminder"
+msgstr "Order reminder"
+
+msgid "today"
+msgstr "today"
+
+msgid "tomorrow"
+msgstr "tomorrow"
+
+msgid "Pickup_reminder_for"
+msgstr "Pickup reminder for"
+
+msgid "{0,plural,=1{1_delivery_note_was} other{#_delivery_notes_were}}_generated_successfully."
+msgstr "{0,plural,=1{1 delivery note was} other{# delivery notes were}} generated successfully."
+
+msgid "Delivery_note_for_{0}"
+msgstr "Delivery note for {0}"
+
+msgid "{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully."
+msgstr "{0,plural,=1{1 invoice was} other{# invoices were}} generated successfully."
+
+msgid "to_(time_context)"
+msgstr "to"
+
+msgid "Generated_invoices"
+msgstr "Generated invoices"
+
+msgid "Invoices_for_{0}_have_been_sent"
+msgstr "Invoices for {0} have been sent"
+
+msgid "{0,plural,=1{1_product} other{#_products}}"
+msgstr "{0,plural,=1{1 product} other{# products}}"
+
+msgid "Manufacturer"
+msgstr "Manufacturer"
+
+msgid "Invoice_number_abbreviation"
+msgstr "Invoice nº"
+
+msgid "Sent"
+msgstr "Sent"
+
+msgid "Products"
+msgstr "Products"
+
+msgid "Total_sum"
+msgstr "Total sum"
+
+msgid "Sent_order_lists"
+msgstr "Sent order lists"
+
+msgid "Delivery_day"
+msgstr "Delivery day"
+
msgid "You_have_been_signed_out."
msgstr "You have been signed out."
@@ -162,6 +231,36 @@ msgstr "New products"
msgid "Search"
msgstr "Search"
+msgid "Your_order_has_been_placed_succesfully."
+msgstr "Your order was placed successfully."
+
+msgid "{0}_has_placed_a_new_order_({1})."
+msgstr "{0} has placed a new order ({1})."
+
+msgid "Instant_order_({0})_successfully_placed_for_{1}."
+msgstr "Instant order ({0}) successfully placed for {1}."
+
+msgid "Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}"
+msgstr "Instant order ({0}) successfully placed for {1}. The following manufacturers were notified: {2}"
+
+msgid "Pickup_day"
+msgstr "Pickup day"
+
+msgid "Thank_you_for_your_purchase!"
+msgstr "Thank you for your purchase!"
+
+msgid "Sign_out"
+msgstr "Sign out"
+
+msgid "Continue_shopping"
+msgstr "Continue shopping"
+
+msgid "Print_receipt"
+msgstr "Print receipt"
+
+msgid "{0}_has_placed_a_new_order_for_{1}_({2})."
+msgstr "{0} has placed a new order for {1} ({2})."
+
msgid "Please_add_credit_({0})_(minimal_credit_is_{1})."
msgstr "Please add new credit, your credit minus cart equals {0}. You can place orders until {1}."
@@ -210,36 +309,6 @@ msgstr "{0} has activated the delivery break and the product {1} cannot be order
msgid "Errors_occurred."
msgstr "Errors occurred."
-msgid "Your_order_has_been_placed_succesfully."
-msgstr "Your order was placed successfully."
-
-msgid "{0}_has_placed_a_new_order_({1})."
-msgstr "{0} has placed a new order ({1})."
-
-msgid "Instant_order_({0})_successfully_placed_for_{1}."
-msgstr "Instant order ({0}) successfully placed for {1}."
-
-msgid "Instant_order_({0})_successfully_placed_for_{1}._The_following_manufacturers_were_notified:_{2}"
-msgstr "Instant order ({0}) successfully placed for {1}. The following manufacturers were notified: {2}"
-
-msgid "Pickup_day"
-msgstr "Pickup day"
-
-msgid "Thank_you_for_your_purchase!"
-msgstr "Thank you for your purchase!"
-
-msgid "Sign_out"
-msgstr "Sign out"
-
-msgid "Continue_shopping"
-msgstr "Continue shopping"
-
-msgid "Print_receipt"
-msgstr "Print receipt"
-
-msgid "{0}_has_placed_a_new_order_for_{1}_({2})."
-msgstr "{0} has placed a new order for {1} ({2})."
-
msgid "Notification_about_instant_order_order"
msgstr "Notification about instant order"
@@ -255,6 +324,9 @@ msgstr "You can unsubscribe this e-mail in the manufacturer's se
msgid "Your_purchase"
msgstr "Your purchase"
+msgid "New_order_comment__was_written_by_{0}_for_{1}"
+msgstr "New order comment was written by {0} for {1}"
+
msgid "Order_confirmation"
msgstr "Order confirmation"
@@ -279,6 +351,9 @@ msgstr "Your email address was already activated or the activation code was not
msgid "Your_email_address_has_been_activated_successfully."
msgstr "Your email address has been activated successfully."
+msgid "Filename_Terms-of-use"
+msgstr "Terms-of-use"
+
msgid "Your_email_address_has_been_activated_successfully._Your_password_has_been_sent_to_you."
msgstr "Your email address has been activated successfully, your password has been sent to you."
@@ -624,12 +699,15 @@ msgstr "Base price"
msgid "Price_is_automatically_adapted_after_save."
msgstr "Price will be automatically adapted after saving."
-msgid "The_field_is_red_if_weight_not_yet_adapted."
-msgstr "The field appears read if the weight has not yet been adapted."
-
msgid "Do_not_automatically_apapt_price_just_change_weight."
msgstr "Do not automatically adapt price, just change the weight."
+msgid "Calculator"
+msgstr "Calculator"
+
+msgid "Example_given_abbr"
+msgstr "E.g."
+
msgid "Really_delete_ordered_product?"
msgstr "Really cancel the ordered product?"
@@ -654,17 +732,11 @@ msgstr "Cancel product!"
msgid "Please_only_cancel_if_ok_for_manufacturer!"
msgstr "Please only cancel if this is ok for the manfacturer!"
-msgid "Really_delete_selected_product?"
-msgstr "Really cancel the selected product?"
-
-msgid "Really_delete_selected_products?"
-msgstr "Really cancel the selected products?"
+msgid "You_selected_1_product."
+msgstr "You selected 1 product."
-msgid "You_selected_1_product"
-msgstr "You selected 1 product"
-
-msgid "You_selected_{0}_products"
-msgstr "You selected {0} products"
+msgid "You_selected_{0}_products."
+msgstr "You selected {0} products."
msgid "Adapt_amount_reason_is_mandatory."
msgstr "Please tell us, why you want to decrease the amount."
@@ -720,6 +792,12 @@ msgstr "Do you really want to show product {0} as \"new\"?"
msgid "Really_do_not_show_product_{0}_as_new?"
msgstr "Do you really want to show product {0} as \"not new\"?"
+msgid "Activate"
+msgstr "Activate"
+
+msgid "Deactivate"
+msgstr "Deactivate"
+
msgid "Activate_product"
msgstr "Activate product"
@@ -792,6 +870,9 @@ msgstr "Be careful, you cannot undo this action!"
msgid "Errors_occurred_while_member_was_deleted"
msgstr "Errors occurred while member was deleted"
+msgid "Errors_occurred_while_product_status_was_changed"
+msgstr "Errors occurred while the product status was changed"
+
msgid "Delete_products?"
msgstr "Delete products?"
@@ -901,7 +982,7 @@ msgid "Add_product_feedback"
msgstr "Write feedback to product"
msgid "Add_product_feedback_explanation_text_{0}."
-msgstr "Your feedback will be sent to the manufacturer {0} after successful saving, you can't change it then. The manufacturer gets your e-mail address so that he can write back."
+msgstr "Your feedback will be sent to the manufacturer {0} after successful saving, you can't change it then. The manufacturer gets your name and your e-mail address so that he can write back."
msgid "Tip:_Change_delivery_rhythm_for_multiple_products:_Select_checkboxes_and_click_bottom_button."
msgstr "Tip: You can change the delivery rhythm for multiple products by selecting them. Then click the bottom button."
@@ -951,6 +1032,12 @@ msgstr "back"
msgid "Send_email_to_member"
msgstr "Send email to member"
+msgid "Send_email_to_both_members"
+msgstr "Send email to both members"
+
+msgid "Change_status"
+msgstr "Change status"
+
msgid "Were_the_products_picked_up?"
msgstr "Were the products picked up?"
@@ -1047,9 +1134,6 @@ msgstr "Order possible until amount of"
msgid "zero_or_smaller_zero"
msgstr "0 or smaller than 0"
-msgid "Manufacturer"
-msgstr "Manufacturer"
-
msgid "For_manufacturers_and_contact_persons._Can_be_changed_in_manufacturer_settings."
msgstr "For manufacturers and contact persons. Can be changed in the manufacturer settings."
@@ -1071,9 +1155,6 @@ msgstr "Change delivery rhythm"
msgid "First_delivery_day"
msgstr "First delivery day"
-msgid "Delivery_day"
-msgstr "Delivery day"
-
msgid "First_delivery_day_info_(one_product)."
msgstr "The product can be ordered from now on."
@@ -1260,9 +1341,6 @@ msgstr "Invoice"
msgid "Cancellation_invoice"
msgstr "Cancellation invoice"
-msgid "Filename_Information-about-right-of-withdrawal"
-msgstr "Informationen-ueber-Ruecktrittsrecht"
-
msgid "ID"
msgstr ""
@@ -1272,9 +1350,6 @@ msgstr "Price excl."
msgid "Price_incl."
msgstr "Price incl."
-msgid "Total_sum"
-msgstr "Total sum"
-
msgid "Price_net"
msgstr "Price net"
@@ -1314,9 +1389,36 @@ msgstr "Sum tax"
msgid "Sum_price_incl."
msgstr "Sum price incl."
+msgid "Filename_Information-about-right-of-withdrawal"
+msgstr "Informationen-ueber-Ruecktrittsrecht"
+
msgid "You_are_not_signed_in."
msgstr "Your are not signed in."
+msgid "BackupDatabaseCronjob"
+msgstr "Backup database"
+
+msgid "CheckCreditBalanceCronjob"
+msgstr "Credit reminder"
+
+msgid "EmailOrderReminderCronjob"
+msgstr "Order reminder"
+
+msgid "PickupReminderCronjob"
+msgstr "Pickup reminder"
+
+msgid "SendOrderListsCronjob"
+msgstr "Order lists"
+
+msgid "SendInvoicesToCustomersCronjob"
+msgstr "Invoices"
+
+msgid "SendInvoicesToManufacturersCronjob"
+msgstr "Invoices"
+
+msgid "SendDeliveryNotesCronjob"
+msgstr "Delivery notes"
+
msgid "Action_Log_Invoice_added"
msgstr "Invoice: Added"
@@ -1575,6 +1677,9 @@ msgstr "Feedback approved"
msgid "Action_Log_Feedback_deleted"
msgstr "Feedback deleted"
+msgid "Action_Log_Cronjob_changed"
+msgstr "Cronjob: Changed"
+
msgid "Action_Log_Cronjob_database_backup_done"
msgstr "Cronjob: Database backup done"
@@ -1692,6 +1797,9 @@ msgstr "The product with id {0} does not exist."
msgid "The_attribute_does_not_exist:_{0}"
msgstr "The attribute does not exist: {0}"
+msgid "Please_accept_the_information_about_right_of_withdrawal"
+msgstr "Please accept the information about the right of withdrawal."
+
msgid "Please_accept_the_information_about_right_of_withdrawal."
msgstr "Please accept the information about the right of withdrawal."
@@ -1710,6 +1818,9 @@ msgstr "Please select a pickup day."
msgid "The_pickup_day_is_not_valid."
msgstr "The pickup day is not valid."
+msgid "Delivery_break"
+msgstr "Delivery break"
+
msgid "offline"
msgstr "offline"
@@ -1731,6 +1842,45 @@ msgstr "Please enter the name of the food-coop."
msgid "The_amount_of_characters_needs_to_be_between_{0}_and_{1}."
msgstr "The amount of characters needs to be between {0} and {1}."
+msgid "The_time_interval_is_not_valid."
+msgstr "The time interval is not valid."
+
+msgid "Please_select_a_day_of_month."
+msgstr "Please select a day of month."
+
+msgid "The_day_of_month_is_not_valid."
+msgstr "The day of month is not valid."
+
+msgid "Please_select_a_weekday."
+msgstr "Please select a weekday."
+
+msgid "The_weekday_is_not_valid."
+msgstr "The weekday is not valid."
+
+msgid "daily"
+msgstr "daily"
+
+msgid "weekly"
+msgstr "weekly"
+
+msgid "No_day_of_month_allowed_for_time_interval_{0}."
+msgstr "No day of month allowed for time interval \"{0}\"."
+
+msgid "monthly"
+msgstr "monthly"
+
+msgid "No_weekday_allowed_for_time_interval_{0}."
+msgstr "No weekday allowed for time interval \"{0}\"."
+
+msgid "Please_enter_a_valid_time."
+msgstr "Please enter a valid time."
+
+msgid "The_time_interval_needs_to_equal_\"{0}\""
+msgstr "The time interval needs to equal \"{0}\"."
+
+msgid "Last_day_of_month"
+msgstr "Last day of month"
+
msgid "Please_enter_your_first_name."
msgstr "Please enter your first name."
@@ -1791,9 +1941,6 @@ msgstr "Please enter a valid IBAN."
msgid "Please_enter_a_valid_BIC."
msgstr "Please enter a valid BIC."
-msgid "Delivery_break"
-msgstr "Delivery break"
-
msgid "online"
msgstr "online"
@@ -1803,9 +1950,6 @@ msgstr "Please enter your feedback."
msgid "Please_enter_a_correct_number."
msgstr "Please enter a correct number."
-msgid "{0,plural,=1{1_product} other{#_products}}"
-msgstr "{0,plural,=1{1 product} other{# products}}"
-
msgid "The_amount_(money)_needs_to_be_greater_than_0."
msgstr "The amount needs to be greater than 0."
@@ -1869,6 +2013,9 @@ msgstr "deleted"
msgid "The_price_needs_to_be_greater_or_equal_than_0."
msgstr "The price needs to be greater or equal than 0."
+msgid "Purchase_price"
+msgstr "Purchase price"
+
msgid "The_quantity_needs_to_be_a_number."
msgstr "The quantity needs to be a number."
@@ -1926,63 +2073,6 @@ msgstr "Member"
msgid "Order_lists_for_the_day"
msgstr "Order lists for"
-msgid "Runtime"
-msgstr "Runtime"
-
-msgid "Database_backup_successful"
-msgstr "Database backup successful"
-
-msgid "Please_add_credit"
-msgstr "Please add credit"
-
-msgid "Sum"
-msgstr "Sum"
-
-msgid "Sent_emails"
-msgstr "Sent emails"
-
-msgid "Order_reminder"
-msgstr "Order reminder"
-
-msgid "today"
-msgstr "today"
-
-msgid "tomorrow"
-msgstr "tomorrow"
-
-msgid "Pickup_reminder_for"
-msgstr "Pickup reminder for"
-
-msgid "{0,plural,=1{1_delivery_note_was} other{#_delivery_notes_were}}_generated_successfully."
-msgstr "{0,plural,=1{1 delivery note was} other{# delivery notes were}} generated successfully."
-
-msgid "Delivery_note_for_{0}"
-msgstr "Delivery note for {0}"
-
-msgid "{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully."
-msgstr "{0,plural,=1{1 invoice was} other{# invoices were}} generated successfully."
-
-msgid "to_(time_context)"
-msgstr "to"
-
-msgid "Generated_invoices"
-msgstr "Generated invoices"
-
-msgid "Invoices_for_{0}_have_been_sent"
-msgstr "Invoices for {0} have been sent"
-
-msgid "Invoice_number_abbreviation"
-msgstr "Invoice nº"
-
-msgid "Sent"
-msgstr "Sent"
-
-msgid "Products"
-msgstr "Products"
-
-msgid "Sent_order_lists"
-msgstr "Sent order lists"
-
msgid "Customer_adds_payment_manually"
msgstr "Members add their payments manually"
@@ -2008,7 +2098,7 @@ msgid "Order_adaptions"
msgstr "Order adaptions"
msgid "My_data"
-msgstr ""
+msgstr "My data"
msgid "Change_password"
msgstr "Change password"
@@ -2025,9 +2115,6 @@ msgstr "Use purchase price for shopping"
msgid "Shopping_with_zero_price"
msgstr "Use zero price for shopping"
-msgid "weekly"
-msgstr "weekly"
-
msgid "every_{0}_week"
msgstr "every {0} week"
@@ -2040,6 +2127,12 @@ msgstr "every last {0} of a month"
msgid "bulk_order"
msgstr "bulk order"
+msgid "midnight"
+msgstr "midnight"
+
+msgid "default_value"
+msgstr "default value"
+
msgid "yes"
msgstr "yes"
@@ -2097,6 +2190,15 @@ msgstr "Admin"
msgid "Superadmin"
msgstr "Superadmin"
+msgid "Configurations"
+msgstr "Settings"
+
+msgid "Cronjobs"
+msgstr "Cronjobs"
+
+msgid "Tax_rates"
+msgstr "Tax rates"
+
msgid "Credit_overview"
msgstr "Credit overview"
@@ -2151,12 +2253,6 @@ msgstr "{0,plural,=1{1 minute} other{# minutes}}"
msgid "{0,plural,=1{1_second} other{#_seconds}}"
msgstr "{0,plural,=1{1 second} other{# seconds}}"
-msgid "midnight"
-msgstr "midnight"
-
-msgid "default_value"
-msgstr "default value"
-
msgid "price_will_be_adapted."
msgstr "price will be adapted."
@@ -2400,6 +2496,9 @@ msgstr "I promise to pick up the products on the selected pickup day and I will
msgid "No_order_possible"
msgstr "No order possible"
+msgid "{0}_times_ordered_for_pickup_day_{1}."
+msgstr "{0} times ordered for pickup day {1}."
+
msgid "available"
msgstr "available"
@@ -2676,6 +2775,19 @@ msgstr "placed on"
msgid "Prices_are_including_vat."
msgstr "The prices include the VAT."
+#~ msgid "The_field_is_red_if_weight_not_yet_adapted."
+#~ msgstr "The field appears read if the weight has not yet been adapted."
+
+#~ msgid "Really_delete_selected_product?"
+#~ msgstr "Really cancel the selected product?"
+
+#~ msgid "Really_delete_selected_products?"
+#~ msgstr "Really cancel the selected products?"
+
+#, fuzzy
+#~ msgid "The_day_of_month_is_required."
+#~ msgstr "The day of month is not valid."
+
#~ msgid "No_stock_products_selected."
#~ msgstr "No stock products were selected."
@@ -2719,9 +2831,6 @@ msgstr "The prices include the VAT."
#~ msgid "Mouseover_shows_the_paid_amont_in_{0}."
#~ msgstr "Mouseover shows the paid amount in {0}."
-#~ msgid "Order_contains_amount_in_{0}."
-#~ msgstr "Order was partly paid in {0}."
-
#~ msgid "Paying_with_time_account_name_{0}"
#~ msgstr "Account of {0}"
@@ -2794,9 +2903,6 @@ msgstr "The prices include the VAT."
#~ msgid "Surcharge"
#~ msgstr "Surcharge"
-#~ msgid "The_discount_is_not_valid."
-#~ msgstr "The discount is not valid."
-
#~ msgid "No_discount_normal_selling_prices_are_used"
#~ msgstr "No discount - normal selling prices are used for shopping"
@@ -2832,9 +2938,6 @@ msgstr "The prices include the VAT."
#~ msgid "Your_email_address_was_activated"
#~ msgstr "Your email address was successfully activated and you are already signed in."
-#~ msgid "Filename_Terms-of-use"
-#~ msgstr "Terms-of-use"
-
#~ msgid "Generate_orders_as_pdf?"
#~ msgstr "Generate orders as PDF?"
diff --git a/src/Application.php b/src/Application.php
index 8af52ae343..f36ec67539 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -17,12 +17,13 @@
namespace App;
use Cake\Core\Configure;
-use Cake\Core\Exception\MissingPluginException;
-use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
+use Cake\Core\Exception\MissingPluginException;
+use Cake\Error\Middleware\ErrorHandlerMiddleware;
+use Cake\Http\Middleware\CsrfProtectionMiddleware;
/**
* Application setup class.
@@ -42,7 +43,10 @@ public function bootstrap(): void
{
// Call parent to load bootstrap from files.
parent::bootstrap();
+
if (Configure::read('debug')) {
+ $this->addPlugin('Bake');
+ Configure::write('DebugKit.forceEnable', true);
$this->addPlugin('DebugKit', ['bootstrap' => true]);
}
@@ -56,6 +60,8 @@ public function bootstrap(): void
'autoload' => true
]);
+ require_once $this->configDir . 'bootstrap_locale.php';
+
if (Configure::read('appDb.FCS_NETWORK_PLUGIN_ENABLED')) {
$this->addPlugin('Network', [
'routes' => true,
@@ -63,7 +69,6 @@ public function bootstrap(): void
]);
}
- // Load more plugins here
}
/**
@@ -74,6 +79,16 @@ public function bootstrap(): void
*/
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
+
+ $csrf = new CsrfProtectionMiddleware();
+
+ // Token check will be skipped when callback returns `true`.
+ $csrf->skipCheckCallback(function ($request) {
+ if (in_array($request->getPath(), ['/api/getProducts.json', '/api/updateProducts.json', '/api/getOrders.json'])) {
+ return true;
+ }
+ });
+
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
@@ -84,6 +99,9 @@ public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
'cacheTime' => Configure::read('Asset.cacheTime'),
]))
+ // Ensure routing middleware is added to the queue before CSRF protection middleware.
+ ->add($csrf)
+
// Add routing middleware.
// If you have a large number of routes connected, turning on routes
// caching in production could improve performance. For that when
diff --git a/src/Assets/AssetsProvider.php b/src/Assets/AssetsProvider.php
index 9c43a64e42..9114aecca7 100644
--- a/src/Assets/AssetsProvider.php
+++ b/src/Assets/AssetsProvider.php
@@ -1,4 +1,6 @@
* @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com
* @link https://www.foodcoopshop.com
*/
-class AppShell extends Shell
+class AppCommand extends Command
{
public $timeStart;
public $timeEnd;
- public function main()
- {
- $this->Customer = $this->getTableLocator()->get('Customers');
- $this->ActionLog = $this->getTableLocator()->get('ActionLogs');
- }
-
public function startTimeLogging()
{
$this->timeStart = microtime(true);
@@ -47,17 +42,4 @@ public function getRuntime()
return __('Runtime') . ': ' . Configure::read('app.timeHelper')->convertSecondsInMinutesAndSeconds($time);
}
- public function out($message, int $newlines = 1, int $level = Shell::NORMAL): ?int
- {
- if ($this->isCalledFromUnitTest()) {
- return null;
- } else {
- return parent::out($message, $newlines, $level);
- }
- }
-
- private function isCalledFromUnitTest()
- {
- return php_sapi_name() == 'cli' && $_SERVER['argv'][0] && preg_match('/phpunit/', $_SERVER['argv'][0]);
- }
-}
+}
\ No newline at end of file
diff --git a/src/Command/BackupDatabaseCommand.php b/src/Command/BackupDatabaseCommand.php
new file mode 100644
index 0000000000..59b2e02dd0
--- /dev/null
+++ b/src/Command/BackupDatabaseCommand.php
@@ -0,0 +1,95 @@
+
+ * @copyright Copyright (c) Mario Rothauer, https://www.rothauer-it.com
+ * @link https://www.foodcoopshop.com
+ */
+class BackupDatabaseCommand extends AppCommand
+{
+
+ public $ActionLog;
+
+ public function execute(Arguments $args, ConsoleIo $io)
+ {
+
+ ini_set('max_execution_time', 300);
+ ini_set('memory_limit', '256M');
+
+ $this->startTimeLogging();
+
+ $dbConfig = ConnectionManager::getConfig('default');
+
+ $backupdir = ROOT . DS . 'files_private' . DS . 'db-backups';
+ $preparedHostWithoutProtocol = Configure::read('app.htmlHelper')->getHostWithoutProtocol(Configure::read('App.fullBaseUrl'));
+ $preparedHostWithoutProtocol = str_replace('www.', '', $preparedHostWithoutProtocol);
+ $preparedHostWithoutProtocol = StringComponent::slugify($preparedHostWithoutProtocol);
+ $filename = $backupdir . DS . $preparedHostWithoutProtocol . '-' . date('Y-m-d_H-i-s', time()) . '.bz2';
+
+ if (! is_dir($backupdir)) {
+ $io->out(' ', 1);
+ $io->out('Will create "' . $backupdir . '" directory!');
+ if (mkdir($backupdir, 0755, true)) {
+ $io->out('Directory created!');
+ }
+ }
+
+ $dsnString = "mysql:host=". $dbConfig['host'].";dbname=".$dbConfig['database'];
+ if (isset($dbConfig['port'])) {
+ $dsnString .= ";port=".$dbConfig['port'];
+ }
+
+ $settings = [
+ 'default-character-set' => 'utf8mb4',
+ 'add-drop-table' => true,
+ 'compress' => 'Bzip2',
+ 'exclude-tables' => [
+ 'queued_jobs',
+ ],
+ ];
+ $dump = new \Druidfi\Mysqldump\Mysqldump($dsnString, $dbConfig['username'], $dbConfig['password'], $settings);
+ $dump->start($filename);
+
+ $message = __('Database_backup_successful') . ' ('.Number::toReadableSize(filesize($filename)).').';
+
+ // 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.fullBaseUrl'))
+ ->setAttachments([
+ $filename
+ ])
+ ->send();
+ $io->out($message);
+
+ $this->stopTimeLogging();
+
+ $this->ActionLog = $this->getTableLocator()->get('ActionLogs');
+ $this->ActionLog->customSave('cronjob_backup_database', 0, 0, '', $message . ' ' . $this->getRuntime());
+ $io->out($this->getRuntime());
+
+ return static::CODE_SUCCESS;
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/Shell/ChangeWeeklyPickupDayByOneDayShell.php b/src/Command/ChangeWeeklyPickupDayByOneDayCommand.php
similarity index 74%
rename from src/Shell/ChangeWeeklyPickupDayByOneDayShell.php
rename to src/Command/ChangeWeeklyPickupDayByOneDayCommand.php
index 655eff0605..8091398d06 100644
--- a/src/Shell/ChangeWeeklyPickupDayByOneDayShell.php
+++ b/src/Command/ChangeWeeklyPickupDayByOneDayCommand.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\Shell;
+use Cake\Command\Command;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use App\Lib\Error\Exception\InvalidParameterException;
-class ChangeWeeklyPickupDayByOneDayShell extends Shell
+class ChangeWeeklyPickupDayByOneDayCommand extends Command
{
- public function main()
+ public $Product;
+ public $Configuration;
+
+ public function execute(Arguments $args, ConsoleIo $io)
{
- if (empty($this->args)) {
+ if (empty($args->getArguments())) {
throw new InvalidParameterException('args not set');
}
- if (!in_array($this->args[0], ['increase', 'decrease'])) {
+ if (!in_array($args->getArgumentAt(0), ['increase', 'decrease'])) {
throw new InvalidParameterException('args wrong');
}
@@ -40,10 +47,10 @@ public function main()
"UPDATE fcs_configuration SET value = :newWeeklyPickupDay WHERE name = 'FCS_WEEKLY_PICKUP_DAY';"
);
- if ($this->args[0] == 'increase') {
+ if ($args->getArgumentAt(0) == 'increase') {
$newWeeklyPickupDay = Configure::read('app.timeHelper')->getNthWeekdayAfterWeekday(1, Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY'));
}
- if ($this->args[0] == 'decrease') {
+ if ($args->getArgumentAt(0) == 'decrease') {
$newWeeklyPickupDay = Configure::read('app.timeHelper')->getNthWeekdayBeforeWeekday(1, Configure::read('appDb.FCS_WEEKLY_PICKUP_DAY'));
}
@@ -54,10 +61,10 @@ public function main()
$products = $this->Product->find('all');
foreach($products as $product) {
- if ($this->args[0] == 'increase') {
+ if ($args->getArgumentAt(0) == 'increase') {
$newDeliveryRhythmSendOrderListWeekday = Configure::read('app.timeHelper')->getNthWeekdayAfterWeekday(1, $product->delivery_rhythm_send_order_list_weekday);
}
- if ($this->args[0] == 'decrease') {
+ if ($args->getArgumentAt(0) == 'decrease') {
$newDeliveryRhythmSendOrderListWeekday = Configure::read('app.timeHelper')->getNthWeekdayBeforeWeekday(1, $product->delivery_rhythm_send_order_list_weekday);
}
$statement = $this->Product->getConnection()->prepare(
@@ -70,9 +77,10 @@ public function main()
$statement->execute($params);
}
- $this->out('Changed FCS_WEEKLY_PICKUP_DAY to ' . Configure::read('app.timeHelper')->getWeekdayName($newWeeklyPickupDay) . '.');
+ $io->out('Changed FCS_WEEKLY_PICKUP_DAY to ' . Configure::read('app.timeHelper')->getWeekdayName($newWeeklyPickupDay) . '.');
+
+ return static::CODE_SUCCESS;
}
}
-
diff --git a/src/Shell/CheckCreditBalanceShell.php b/src/Command/CheckCreditBalanceCommand.php
similarity index 87%
rename from src/Shell/CheckCreditBalanceShell.php
rename to src/Command/CheckCreditBalanceCommand.php
index 99514a3348..14a81e8cb3 100644
--- a/src/Shell/CheckCreditBalanceShell.php
+++ b/src/Command/CheckCreditBalanceCommand.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\Mailer\AppMailer;
use Cake\Core\Configure;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
+
-class CheckCreditBalanceShell extends AppShell
+class CheckCreditBalanceCommand extends AppCommand
{
- public function main()
+ public $ActionLog;
+ public $Customer;
+
+ public function execute(Arguments $args, ConsoleIo $io)
{
- parent::main();
if (!Configure::read('app.htmlHelper')->paymentIsCashless()) {
return;
@@ -31,6 +38,7 @@ public function main()
$this->startTimeLogging();
+ $this->Customer = $this->getTableLocator()->get('Customers');
$this->Customer->dropManufacturersInNextFind();
$conditions = [
'Customers.active' => 1,
@@ -103,12 +111,14 @@ public function main()
$this->stopTimeLogging();
+ $this->ActionLog = $this->getTableLocator()->get('ActionLogs');
$this->ActionLog->customSave('cronjob_check_credit_balance', 0, 0, '', $outString . ' ' . $this->getRuntime());
- $this->out($outString);
- $this->out($this->getRuntime());
+ $io->out($outString);
+ $io->out($this->getRuntime());
- return true;
+ return static::CODE_SUCCESS;
}
+
}
diff --git a/src/Shell/EmailOrderReminderShell.php b/src/Command/EmailOrderReminderCommand.php
similarity index 66%
rename from src/Shell/EmailOrderReminderShell.php
rename to src/Command/EmailOrderReminderCommand.php
index 3b0e11c62f..2f1f6ed490 100644
--- a/src/Shell/EmailOrderReminderShell.php
+++ b/src/Command/EmailOrderReminderCommand.php
@@ -1,4 +1,6 @@
getSendOrderListsDay() -1 or -2
* eg: Order lists are sent on Wednesday => EmailOrderReminder can be called on Tuesday or Monday
*
- * @since FoodCoopShop 1.0.0
+ * @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
*/
-namespace App\Shell;
+namespace App\Command;
+use App\Lib\DeliveryRhythm\DeliveryRhythm;
use App\Mailer\AppMailer;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use Cake\Database\Expression\QueryExpression;
-class EmailOrderReminderShell extends AppShell
+class EmailOrderReminderCommand extends AppCommand
{
- public function main()
+ public $cronjobRunDay;
+ public $ActionLog;
+ public $Customer;
+ public $Product;
+
+ public function execute(Arguments $args, ConsoleIo $io)
{
- // $this->cronjobRunDay can is set in unit test
- if (!isset($this->args[0])) {
+ if (!$args->getArgumentAt(0)) {
$this->cronjobRunDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase();
} else {
- $this->cronjobRunDay = $this->args[0];
+ $this->cronjobRunDay = $args->getArgumentAt(0);
}
if (! Configure::read('app.emailOrderReminderEnabled')) {
- return true;
+ return static::CODE_SUCCESS;
}
- $nextDeliveryDay = Configure::read('app.timeHelper')->getNextDeliveryDay(strtotime($this->cronjobRunDay));
+ $productsTable = $this->getTableLocator()->get('Products');
+ $dummyProduct = $productsTable->newEntity([
+ 'delivery_rhythm_type' => 'week',
+ 'delivery_rhythm_count' => '1',
+ 'is_stock_product' => '0',
+ ]);
+ $nextDeliveryDay = DeliveryRhythm::getNextPickupDayForProduct($dummyProduct, $this->cronjobRunDay);
+
if (Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL') != '') {
$this->Product = $this->getTableLocator()->get('Products');
- if ($this->Product->deliveryBreakEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $nextDeliveryDay)) {
- return true;
+ if ($this->Product->deliveryBreakGlobalEnabled(Configure::read('appDb.FCS_NO_DELIVERY_DAYS_GLOBAL'), $nextDeliveryDay)) {
+ return static::CODE_SUCCESS;
}
}
- parent::main();
-
$this->startTimeLogging();
$conditions = [
'Customers.email_order_reminder_enabled' => 1,
- 'Customers.active' => 1
+ 'Customers.active' => 1,
];
+ $this->Customer = $this->getTableLocator()->get('Customers');
$conditions[] = $this->Customer->getConditionToExcludeHostingUser();
$this->Customer->dropManufacturersInNextFind();
@@ -86,7 +101,7 @@ public function main()
->setViewVars([
'customer' => $customer,
'newsletterCustomer' => $customer,
- 'lastOrderDayAsString' => (Configure::read('app.timeHelper')->getSendOrderListsWeekday() - date('N', strtotime($this->cronjobRunDay))) == 1 ? __('today') : __('tomorrow')
+ 'lastOrderDayAsString' => (DeliveryRhythm::getSendOrderListsWeekday() - date('N', strtotime($this->cronjobRunDay))) == 1 ? __('today') : __('tomorrow')
])
->addToQueue();
@@ -99,12 +114,13 @@ public function main()
$this->stopTimeLogging();
+ $this->ActionLog = $this->getTableLocator()->get('ActionLogs');
$this->ActionLog->customSave('cronjob_email_order_reminder', 0, 0, '', $outString . ' ' . $this->getRuntime());
- $this->out($outString);
- $this->out($this->getRuntime());
+ $io->out($outString);
+ $io->out($this->getRuntime());
- return true;
+ return static::CODE_SUCCESS;
}
}
diff --git a/src/Shell/PickupReminderShell.php b/src/Command/PickupReminderCommand.php
similarity index 81%
rename from src/Shell/PickupReminderShell.php
rename to src/Command/PickupReminderCommand.php
index db7176e1f4..c87c3316ab 100644
--- a/src/Shell/PickupReminderShell.php
+++ b/src/Command/PickupReminderCommand.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\Mailer\AppMailer;
use Cake\Core\Configure;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\Database\Expression\QueryExpression;
+use App\Lib\DeliveryRhythm\DeliveryRhythm;
-class PickupReminderShell extends AppShell
+class PickupReminderCommand extends AppCommand
{
public $cronjobRunDay;
+ public $Customer;
+ public $OrderDetail;
+ public $ActionLog;
- public function main()
+ public function execute(Arguments $args, ConsoleIo $io)
{
- parent::main();
-
- // $this->cronjobRunDay can is set in unit test
- if (!isset($this->args[0])) {
+ if (!$args->getArgumentAt(0)) {
$this->cronjobRunDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase();
} else {
- $this->cronjobRunDay = $this->args[0];
+ $this->cronjobRunDay = $args->getArgumentAt(0);
}
$this->startTimeLogging();
@@ -42,6 +47,7 @@ public function main()
'Customers.pickup_day_reminder_enabled' => 1,
'Customers.active' => 1,
];
+ $this->Customer = $this->getTableLocator()->get('Customers');
$conditions[] = $this->Customer->getConditionToExcludeHostingUser();
$this->Customer->dropManufacturersInNextFind();
@@ -54,7 +60,7 @@ public function main()
$customers = $this->Customer->sortByVirtualField($customers, 'name');
$this->OrderDetail = $this->getTableLocator()->get('OrderDetails');
- $nextPickupDay = Configure::read('app.timeHelper')->getDeliveryDay(strtotime($this->cronjobRunDay));
+ $nextPickupDay = DeliveryRhythm::getDeliveryDay(strtotime($this->cronjobRunDay));
$formattedPickupDay = Configure::read('app.timeHelper')->getDateFormattedWithWeekday($nextPickupDay);
$diffOrderAndPickupInDays = 6;
@@ -103,12 +109,13 @@ public function main()
$this->stopTimeLogging();
+ $this->ActionLog = $this->getTableLocator()->get('ActionLogs');
$this->ActionLog->customSave('cronjob_pickup_reminder', 0, 0, '', $outString . ' ' . $this->getRuntime());
- $this->out($outString);
- $this->out($this->getRuntime());
+ $io->out($outString);
+ $io->out($this->getRuntime());
- return true;
+ return static::CODE_SUCCESS;
}
}
diff --git a/src/Shell/SavedLocalizedJsAsStaticFileShell.php b/src/Command/SavedLocalizedJsAsStaticFileCommand.php
similarity index 64%
rename from src/Shell/SavedLocalizedJsAsStaticFileShell.php
rename to src/Command/SavedLocalizedJsAsStaticFileCommand.php
index 860a7dde4c..5300199f41 100644
--- a/src/Shell/SavedLocalizedJsAsStaticFileShell.php
+++ b/src/Command/SavedLocalizedJsAsStaticFileCommand.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\Filesystem\File;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\TestSuite\IntegrationTestTrait;
-class SavedLocalizedJsAsStaticFileShell extends AppShell
+class SavedLocalizedJsAsStaticFileCommand extends AppCommand
{
use IntegrationTestTrait;
@@ -28,18 +31,20 @@ class SavedLocalizedJsAsStaticFileShell extends AppShell
*
* this script was written to be executed in the deploy process
* in order to get the javascript content from the tmp installation
- * (and not from app.cakeServerName where the new code is not yet available)
+ * (and not from App.fullBaseUrl where the new code is not yet available)
* the built-in HttpClient from IntegrationTest is used
*
* run this script to generate a static file for production use
*
- * @see AppShell::main()
+ * @see AppCommand::main()
*/
- public function main()
+ public function execute(Arguments $args, ConsoleIo $io)
{
$this->get('/js/localized-javascript.js');
- $jsFile = new File(WWW_ROOT . '/cache/localized-javascript-static.js');
- $jsFile->write($this->_response);
+ $jsFile = fopen(WWW_ROOT . '/cache/localized-javascript-static.js', 'w');
+ fwrite($jsFile, $this->_response->getBody()->__toString());
+ fclose($jsFile);
+ return static::CODE_SUCCESS;
}
}
diff --git a/src/Shell/SendDeliveryNotesShell.php b/src/Command/SendDeliveryNotesCommand.php
similarity index 90%
rename from src/Shell/SendDeliveryNotesShell.php
rename to src/Command/SendDeliveryNotesCommand.php
index 2eb92b7b09..f47e04a956 100644
--- a/src/Shell/SendDeliveryNotesShell.php
+++ b/src/Command/SendDeliveryNotesCommand.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\DeliveryNote\GenerateDeliveryNote;
use App\Mailer\AppMailer;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use Cake\Http\Exception\ForbiddenException;
-class SendDeliveryNotesShell extends AppShell
+class SendDeliveryNotesCommand extends AppCommand
{
public $cronjobRunDay;
+ public $ActionLog;
+ public $Manufacturer;
+ public $OrderDetail;
- public function main()
+ public function execute(Arguments $args, ConsoleIo $io)
{
- parent::main();
if (!Configure::read('appDb.FCS_PURCHASE_PRICE_ENABLED')) {
throw new ForbiddenException();
}
+
$this->startTimeLogging();
- if (!isset($this->args[0])) {
+ if (!$args->getArgumentAt(0)) {
$this->cronjobRunDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase();
} else {
- $this->cronjobRunDay = $this->args[0];
+ $this->cronjobRunDay = $args->getArgumentAt(0);
}
$dateFrom = Configure::read('app.timeHelper')->getFirstDayOfLastMonth($this->cronjobRunDay);
@@ -114,7 +121,7 @@ public function main()
}
- return true;
+ return static::CODE_SUCCESS;
}
diff --git a/src/Shell/SendInvoicesToCustomersShell.php b/src/Command/SendInvoicesToCustomersCommand.php
similarity index 83%
rename from src/Shell/SendInvoicesToCustomersShell.php
rename to src/Command/SendInvoicesToCustomersCommand.php
index a473971365..df3a352a3e 100644
--- a/src/Shell/SendInvoicesToCustomersShell.php
+++ b/src/Command/SendInvoicesToCustomersCommand.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\HelloCash\HelloCash;
use App\Lib\Invoice\GenerateInvoiceToCustomer;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use Cake\Http\Exception\ForbiddenException;
-class SendInvoicesToCustomersShell extends AppShell
+class SendInvoicesToCustomersCommand extends AppCommand
{
public $cronjobRunDay;
+ public $ActionLog;
+ public $Customer;
+ public $Invoice;
- public function main()
+ public function execute(Arguments $args, ConsoleIo $io)
{
- parent::main();
if (!Configure::read('appDb.FCS_SEND_INVOICES_TO_CUSTOMERS')) {
throw new ForbiddenException();
@@ -37,11 +43,10 @@ public function main()
$this->Invoice = $this->getTableLocator()->get('Invoices');
$invoiceToCustomer = new GenerateInvoiceToCustomer();
- // $this->cronjobRunDay can is set in unit test
- if (!isset($this->args[0])) {
+ if (!$args->getArgumentAt(0)) {
$this->cronjobRunDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase();
} else {
- $this->cronjobRunDay = $this->args[0];
+ $this->cronjobRunDay = $args->getArgumentAt(0);
}
$this->Customer->dropManufacturersInNextFind();
@@ -70,7 +75,7 @@ public function main()
if (Configure::read('appDb.FCS_HELLO_CASH_API_ENABLED')) {
$helloCash->generateInvoice($data, $this->cronjobRunDay, false, false);
- sleep(5); // the Hello Cash API handels max 60 requests per minute, one generateInvoice call uses up to 5 requests
+ sleep(4); // the Hello Cash API handels max 60 requests per minute, one generateInvoice call uses up to 5 requests
} else {
$invoiceToCustomer->run($data, $this->cronjobRunDay, false);
}
@@ -84,7 +89,7 @@ public function main()
$message = __('{0,plural,=1{1_invoice_was} other{#_invoices_were}}_generated_successfully.', [$i]);
$this->ActionLog->customSave('invoice_added', 0, 0, 'invoices', $message . ' ' . $this->getRuntime());
- return true;
+ return static::CODE_SUCCESS;
}
diff --git a/src/Shell/SendInvoicesToManufacturersShell.php b/src/Command/SendInvoicesToManufacturersCommand.php
similarity index 95%
rename from src/Shell/SendInvoicesToManufacturersShell.php
rename to src/Command/SendInvoicesToManufacturersCommand.php
index 412f7ecb56..79086949d9 100644
--- a/src/Shell/SendInvoicesToManufacturersShell.php
+++ b/src/Command/SendInvoicesToManufacturersCommand.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\Mailer\AppMailer;
+use Cake\Console\Arguments;
+use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use Cake\Database\Expression\QueryExpression;
use Cake\I18n\FrozenTime;
-class SendInvoicesToManufacturersShell extends AppShell
+class SendInvoicesToManufacturersCommand extends AppCommand
{
public $cronjobRunDay;
+ public $ActionLog;
+ public $OrderDetail;
+ public $Manufacturer;
+ public $QueuedJobs;
- public function main()
+ public function execute(Arguments $args, ConsoleIo $io)
{
- parent::main();
$this->ActionLog = $this->getTableLocator()->get('ActionLogs');
$this->OrderDetail = $this->getTableLocator()->get('OrderDetails');
$this->Manufacturer = $this->getTableLocator()->get('Manufacturers');
- // $this->cronjobRunDay can is set in unit test
- if (!isset($this->args[0])) {
+ if (!$args->getArgumentAt(0)) {
$this->cronjobRunDay = Configure::read('app.timeHelper')->getCurrentDateTimeForDatabase();
} else {
- $this->cronjobRunDay = $this->args[0];
+ $this->cronjobRunDay = $args->getArgumentAt(0);
}
$dateFrom = Configure::read('app.timeHelper')->getFirstDayOfLastMonth($this->cronjobRunDay);
@@ -109,7 +115,7 @@ public function main()
}
$outString .= $actionLogDatas;
$actionLog = $this->ActionLog->customSave('cronjob_send_invoices', 0, 0, '', $outString, new FrozenTime($this->cronjobRunDay));
- $this->out($outString);
+ $io->out($outString);
// 6) trigger queue invoice generation
$this->QueuedJobs = $this->getTableLocator()->get('Queue.QueuedJobs');
@@ -142,7 +148,7 @@ public function main()
->addToQueue();
}
- return true;
+ return static::CODE_SUCCESS;
}
@@ -217,7 +223,6 @@ protected function getActionLogData($manufacturers)
$outString .= '
';
}
-
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/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'); ?>
+
+
+
+
+ getUsername(),
+ $formattedPickupDay,
+ ]) . ':';
+ ?>
+
+
+ ' . $comment . '';
+ ?>
+
+
+
+
+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 '';
- 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 ' ';
}
?>
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 '';
- 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 ' ';
}
?>
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 @@
- 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 geliefert 1,00 € 0,00 € (0%) 1,00 € ');
+
+ }
+
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.comRechnung 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
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 €
[] ID Produkt Hersteller Nettopreis Steuersatz Liefertag [] [] 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
Steuersatz Summe Preis exkl. Summe Steuer Summe 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.comRechnung 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
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 €
[] ID Produkt Hersteller Nettopreis Steuersatz Liefertag [] [] 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
Steuersatz Summe Preis exkl. Summe Steuer Summe 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"
}
}