Commit 43ff4f68 authored by Malcolm Blaney's avatar Malcolm Blaney

Cart module fixed a bug where tabs didn't match up due to special

characters in html. Also missed a case where tracking wasn't turned
back on for previously tracked products. Organiser->Parent() was
returning an empty string when an organisation wasn't found, it now
returns false in this case as the empty string is a valid group.
Other changes were required to allow for this. Writer module needs
to set a default location for posts for Micropub to work. Tried to
improve how tooltips work in Reader module js. Also unread count is
accumulative so need to parse the current unread total before
modifying the page-select button.
parent 375aeb27
Pipeline #44866481 passed with stage
in 1 minute and 15 seconds
...@@ -96,7 +96,11 @@ class Cart extends Base { ...@@ -96,7 +96,11 @@ class Cart extends Base {
if (strpos($tab_names, ',') !== false) { if (strpos($tab_names, ',') !== false) {
$tabs .= '<div id="cart-tabs"><ul id="cart-tabs-list">'; $tabs .= '<div id="cart-tabs"><ul id="cart-tabs-list">';
foreach (explode(',', $tab_names) as $current_tab) { foreach (explode(',', $tab_names) as $current_tab) {
$tab_id = preg_replace('/[^a-z0-9_-]/', '-', strtolower($current_tab)); // The tab names template is stored encoded for display in html, but
// the tabs used below from cart_item_page is not encoded so decode
// here so that tab links match the generated ids.
$decode_current_tab = strtolower(htmlspecialchars_decode($current_tab));
$tab_id = preg_replace('/[^a-z0-9_-]/', '-', $decode_current_tab);
$tabs .= '<li><a href="#cart-tab-' . $tab_id . '">' . $current_tab . $tabs .= '<li><a href="#cart-tab-' . $tab_id . '">' . $current_tab .
'</a></li>'; '</a></li>';
} }
...@@ -669,7 +673,8 @@ class Cart extends Base { ...@@ -669,7 +673,8 @@ class Cart extends Base {
// in two steps because the queries don't join well. Need everything from // in two steps because the queries don't join well. Need everything from
// the cart_items table and only existing values from cart_item_page table. // the cart_items table and only existing values from cart_item_page table.
$query = 'SELECT name, available, tab, item_order FROM cart_item_page ' . $query = 'SELECT name, available, tab, item_order FROM cart_item_page ' .
'WHERE user = "' . $this->owner . '" AND page = "' .$this->user->page.'"'; 'WHERE user = "' . $this->owner . '" AND ' .
'page = "' . $this->user->page . '"';
if ($mysqli_result = $mysqli->query($query)) { if ($mysqli_result = $mysqli->query($query)) {
while ($cart_item_page = $mysqli_result->fetch_assoc()) { while ($cart_item_page = $mysqli_result->fetch_assoc()) {
$current[$cart_item_page['name']] = $current[$cart_item_page['name']] =
...@@ -1589,8 +1594,9 @@ class Cart extends Base { ...@@ -1589,8 +1594,9 @@ class Cart extends Base {
$variable = (int)$_POST['variable']; $variable = (int)$_POST['variable'];
$minimum = price_string((float)$_POST['minimum']); $minimum = price_string((float)$_POST['minimum']);
$download = $mysqli->escape_string(htmlspecialchars($_POST['download'])); $download = $mysqli->escape_string(htmlspecialchars($_POST['download']));
$us_item_tab = htmlspecialchars($_POST['itemTab']); // Note that tabs are not escaped as they are not expected to be displayed
$item_tab = $mysqli->escape_string($us_item_tab); // as they are in html and are required to be used elsewere unescaped.
$item_tab = $mysqli->escape_string($_POST['itemTab']);
$item_order = (int)$_POST['itemOrder']; $item_order = (int)$_POST['itemOrder'];
$available = (int)$_POST['available']; $available = (int)$_POST['available'];
$tracking = (int)$_POST['tracking']; $tracking = (int)$_POST['tracking'];
...@@ -1653,23 +1659,6 @@ class Cart extends Base { ...@@ -1653,23 +1659,6 @@ class Cart extends Base {
else { else {
$this->Log('Cart->SaveItem 5: ' . $mysqli->error); $this->Log('Cart->SaveItem 5: ' . $mysqli->error);
} }
// Also make sure the tab doesn't get modified on the tracked page.
if (!$modified && $this->user->page === $this->Substitute('cart-page')) {
$query = 'SELECT tab FROM cart_item_page WHERE ' .
'user = "' . $this->owner . '" AND name = "' . $name . '" AND ' .
'page = "' . $this->user->page . '"';
if ($mysqli_result = $mysqli->query($query)) {
if ($cart_item_page = $mysqli_result->fetch_assoc()) {
if ($cart_item_page['tab'] !== $us_item_tab) {
$modified = true;
}
}
$mysqli_result->close();
}
else {
$this->Log('Cart->SaveItem 6: ' . $mysqli->error);
}
}
if ($modified) { if ($modified) {
$query = 'UPDATE cart_tracking SET modified = 1 WHERE ' . $query = 'UPDATE cart_tracking SET modified = 1 WHERE ' .
'user = "' . $this->owner . '" AND name = "' . $name . '"'; 'user = "' . $this->owner . '" AND name = "' . $name . '"';
...@@ -2023,7 +2012,7 @@ class Cart extends Base { ...@@ -2023,7 +2012,7 @@ class Cart extends Base {
$modified = false; $modified = false;
$current_tracking = false; $current_tracking = false;
$query = 'SELECT name, modified, tracking FROM cart_tracking WHERE ' . $query = 'SELECT name, modified, tracking FROM cart_tracking WHERE ' .
'user = "' . $user .'" AND product = "' . $product . '" AND ' . 'user = "' . $user . '" AND product = "' . $product . '" AND ' .
'supplier = "' . $supplier . '"'; 'supplier = "' . $supplier . '"';
if ($mysqli_result = $mysqli->query($query)) { if ($mysqli_result = $mysqli->query($query)) {
if ($cart_tracking = $mysqli_result->fetch_assoc()) { if ($cart_tracking = $mysqli_result->fetch_assoc()) {
...@@ -2052,15 +2041,13 @@ class Cart extends Base { ...@@ -2052,15 +2041,13 @@ class Cart extends Base {
} }
// Use the stock name from the product when it doesn't exist here. // Use the stock name from the product when it doesn't exist here.
if ($name === '') { if ($name === '') $name = $product;
$name = $product; $query = 'INSERT INTO cart_tracking VALUES ("' . $user . '", ' .
$query = 'INSERT INTO cart_tracking VALUES ("' . $user . '", ' . '"' . $name . '", "' . $supplier . '", "' . $product . '", 0, 1) ' .
'"' . $name . '", "' . $supplier . '", "' . $product . '", 0, 1) ' . 'ON DUPLICATE KEY UPDATE supplier = "' . $supplier . '", ' .
'ON DUPLICATE KEY UPDATE supplier = "' . $supplier . '", ' . 'product = "' . $product . '", tracking = 1';
'product = "' . $product . '", tracking = 1'; if (!$mysqli->query($query)) {
if (!$mysqli->query($query)) { $this->Log('Cart->UpdateItem 4: ' . $mysqli->error);
$this->Log('Cart->UpdateItem 4: ' . $mysqli->error);
}
} }
// The modified flag is used here to decide which fields can be updated for // The modified flag is used here to decide which fields can be updated for
// existing items. // existing items.
...@@ -2087,15 +2074,8 @@ class Cart extends Base { ...@@ -2087,15 +2074,8 @@ class Cart extends Base {
$query = 'INSERT INTO cart_item_page VALUES ("' . $user . '", ' . $query = 'INSERT INTO cart_item_page VALUES ("' . $user . '", ' .
'"' . $name . '", "' . $page . '", ' . $available . ', ' . '"' . $name . '", "' . $page . '", ' . $available . ', ' .
'"' . $category . '", 0) '; '"' . $category . '", 0) ON DUPLICATE KEY UPDATE ' .
// If this cart item has been modified don't want to update the tab here. 'available = ' . $available . ', tab = "' . $category . '"';
if ($modified) {
$query .= 'ON DUPLICATE KEY UPDATE available = ' . $available;
}
else {
$query .= 'ON DUPLICATE KEY UPDATE available = ' . $available .
', tab = "' . $category . '"';
}
if (!$mysqli->query($query)) { if (!$mysqli->query($query)) {
$this->Log('Cart->UpdateItem 6: ' . $mysqli->error); $this->Log('Cart->UpdateItem 6: ' . $mysqli->error);
} }
......
<?php <?php
// Dobrado Content Management System // Dobrado Content Management System
// Copyright (C) 2018 Malcolm Blaney // Copyright (C) 2019 Malcolm Blaney
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
...@@ -29,6 +29,8 @@ class Contact extends Base { ...@@ -29,6 +29,8 @@ class Contact extends Base {
$this->AddBoxStyle($box_style); $this->AddBoxStyle($box_style);
$organiser = new Organiser($this->user, $this->owner); $organiser = new Organiser($this->user, $this->owner);
$parent = $organiser->Parent();
if ($parent === false) $parent = '';
$content = '<form class="contact-form">' . $content = '<form class="contact-form">' .
'When editing this form, make sure each field you add has a name ' . 'When editing this form, make sure each field you add has a name ' .
'attribute.<br>The field with the name "contact-name" is used on the ' . 'attribute.<br>The field with the name "contact-name" is used on the ' .
...@@ -40,7 +42,7 @@ class Contact extends Base { ...@@ -40,7 +42,7 @@ class Contact extends Base {
'<label>Contact name:</label><input name="contact-name" type="text">' . '<label>Contact name:</label><input name="contact-name" type="text">' .
'</div>' . '</div>' .
'<input name="contact-organisation" ' . '<input name="contact-organisation" ' .
'value="' . $organiser->Parent() . '" type="hidden">' . 'value="' . $parent . '" type="hidden">' .
'<button class="submit">submit</button>' . '<button class="submit">submit</button>' .
'<span class="contact-info" style="display:none;">' . '<span class="contact-info" style="display:none;">' .
'ckeditor removes this span if empty</span>' . 'ckeditor removes this span if empty</span>' .
...@@ -280,12 +282,12 @@ class Contact extends Base { ...@@ -280,12 +282,12 @@ class Contact extends Base {
$mysqli = connect_db(); $mysqli = connect_db();
$query = 'SELECT content FROM contact WHERE user = "' . $user . '" ' . $query = 'SELECT content FROM contact WHERE user = "' . $user . '" ' .
'AND box_id = ' . $id; 'AND box_id = ' . $id;
if ($result = $mysqli->query($query)) { if ($mysqli_result = $mysqli->query($query)) {
if ($contact = $result->fetch_assoc()) { if ($contact = $mysqli_result->fetch_assoc()) {
$content = $escape ? $mysqli->escape_string($contact['content']) : $content = $escape ? $mysqli->escape_string($contact['content']) :
$contact['content']; $contact['content'];
} }
$result->close(); $mysqli_result->close();
} }
else { else {
$this->Log('Contact->PlainContent: ' . $mysqli->error); $this->Log('Contact->PlainContent: ' . $mysqli->error);
......
<?php <?php
// Dobrado Content Management System // Dobrado Content Management System
// Copyright (C) 2018 Malcolm Blaney // Copyright (C) 2019 Malcolm Blaney
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
...@@ -75,11 +75,11 @@ class Groupwizard extends Base { ...@@ -75,11 +75,11 @@ class Groupwizard extends Base {
$mysqli = connect_db(); $mysqli = connect_db();
$query = 'SELECT label FROM modules WHERE user = "admin" AND ' . $query = 'SELECT label FROM modules WHERE user = "admin" AND ' .
'page = "' . $page . '" AND label = "banking" AND deleted = 0'; 'page = "' . $page . '" AND label = "banking" AND deleted = 0';
if ($result = $mysqli->query($query)) { if ($mysqli_result = $mysqli->query($query)) {
if ($result->num_rows === 1) { if ($mysqli_result->num_rows === 1) {
$banking = true; $banking = true;
} }
$result->close(); $mysqli_result->close();
} }
else { else {
$this->Log('Groupwizard->Content: ' . $mysqli->error); $this->Log('Groupwizard->Content: ' . $mysqli->error);
...@@ -171,7 +171,7 @@ class Groupwizard extends Base { ...@@ -171,7 +171,7 @@ class Groupwizard extends Base {
if (count($groups) > 1) { if (count($groups) > 1) {
$organisation = true; $organisation = true;
$parent = $organiser->Parent(); $parent = $organiser->Parent();
$parent_text = $parent === '' ? '' : $parent_text = $parent === false || $parent === '' ? '' :
' in <b>' . ucfirst($parent) . '</b>'; ' in <b>' . ucfirst($parent) . '</b>';
$group_text = $this->user->group === '' ? '' : $group_text = $this->user->group === '' ? '' :
' group, <b>' . ucfirst($this->user->group) . '</b>'; ' group, <b>' . ucfirst($this->user->group) . '</b>';
...@@ -573,9 +573,10 @@ class Groupwizard extends Base { ...@@ -573,9 +573,10 @@ class Groupwizard extends Base {
'<input id="groupwizard-stock-tax-percent" type="text" ' . '<input id="groupwizard-stock-tax-percent" type="text" ' .
'maxlength="5" value="' . $this->Value('stock-tax-percent') . '">' . 'maxlength="5" value="' . $this->Value('stock-tax-percent') . '">' .
'</div>' . '</div>' .
'<p>Edit the default markup to apply to stock supplier prices:<br>' . '<p>Edit the default markup to apply to supplier prices. Leave ' .
'Leave either option empty to hide it on the stock page, to update ' . 'either option empty to hide it on the stock page. You can also ' .
'all prices for a supplier please re-import their stock list.</p>' . 'set the options to \'0\' to show it on the stock page and not ' .
'have it apply markup automatically.</p>' .
'<div class="form-spacing">' . '<div class="form-spacing">' .
'<label for="groupwizard-stock-wholesale-percent">The markup for ' . '<label for="groupwizard-stock-wholesale-percent">The markup for ' .
'wholesale:</label>' . 'wholesale:</label>' .
...@@ -924,13 +925,13 @@ class Groupwizard extends Base { ...@@ -924,13 +925,13 @@ class Groupwizard extends Base {
return ['error' => 'Group name has the wrong format.']; return ['error' => 'Group name has the wrong format.'];
} }
$object = []; $result = [];
$default_group = $this->user->group; $default_group = $this->user->group;
$this->user->group = $group; $this->user->group = $group;
// Check if a group with this name already exists in an organisation. // Check if a group with this name already exists in an organisation.
$organiser = new Organiser($this->user, $this->owner); $organiser = new Organiser($this->user, $this->owner);
if ($organiser->Parent() !== '') { if ($organiser->Parent() !== false) {
$object['error'] = 'Group name is not available.'; $result['error'] = 'Group name is not available.';
} }
else { else {
$invite = new Invite($this->user, $this->owner); $invite = new Invite($this->user, $this->owner);
...@@ -938,14 +939,14 @@ class Groupwizard extends Base { ...@@ -938,14 +939,14 @@ class Groupwizard extends Base {
$_SESSION['purchase-group'] = $this->user->group; $_SESSION['purchase-group'] = $this->user->group;
$_SESSION['purchase-group-changed'] = true; $_SESSION['purchase-group-changed'] = true;
$this->user->group = $default_group; $this->user->group = $default_group;
$object['content'] = $this->Content(0); $result['content'] = $this->Content(0);
} }
else { else {
$object['error'] = 'Group name is not available.'; $result['error'] = 'Group name is not available.';
} }
} }
$this->user->group = $default_group; $this->user->group = $default_group;
return $object; return $result;
} }
private function ChangeGroup() { private function ChangeGroup() {
......
<?php <?php
// Dobrado Content Management System // Dobrado Content Management System
// Copyright (C) 2018 Malcolm Blaney // Copyright (C) 2019 Malcolm Blaney
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as // it under the terms of the GNU Affero General Public License as
...@@ -198,8 +198,9 @@ class Invoice extends Base { ...@@ -198,8 +198,9 @@ class Invoice extends Base {
// Only send orders for the organisation if more than one group. // Only send orders for the organisation if more than one group.
if (count($send_groups) > 1) { if (count($send_groups) > 1) {
$result = $this->NextOrder(true, true, $send_groups); $result = $this->NextOrder(true, true, $send_groups);
$organisation = $organiser->Parent();
if ($organisation === false) $organisation = '';
if ($result['done'] === true) { if ($result['done'] === true) {
$organisation = $organiser->Parent();
$description = 'Cron: sent organisation order'; $description = 'Cron: sent organisation order';
if ($organisation !== '') { if ($organisation !== '') {
$description .= ' for ' . $organisation; $description .= ' for ' . $organisation;
...@@ -209,7 +210,6 @@ class Invoice extends Base { ...@@ -209,7 +210,6 @@ class Invoice extends Base {
} }
else { else {
// Add a notification for unsent orders. // Add a notification for unsent orders.
$organisation = $organiser->Parent();
$description = 'Cron: ' . $result['error']; $description = 'Cron: ' . $result['error'];
if ($organisation !== '') { if ($organisation !== '') {
$description .= ' for ' . $organisation; $description .= ' for ' . $organisation;
...@@ -783,13 +783,12 @@ class Invoice extends Base { ...@@ -783,13 +783,12 @@ class Invoice extends Base {
$sold < 0.01 && $purchase_value > -0.01 && $purchase_value < 0.01) { $sold < 0.01 && $purchase_value > -0.01 && $purchase_value < 0.01) {
continue; continue;
} }
$result['grid'][] = $result['grid'][] = ['name' => $user,
['name' => $user, 'purchases' => price_string($purchase_value),
'purchases' => number_format($purchase_value, 2, '.', ''), 'surcharge' => price_string($surcharge),
'surcharge' => number_format($surcharge, 2, '.', ''), 'sold' => price_string($sold),
'sold' => number_format($sold, 2, '.', ''), 'balance' => price_string($balance * -1),
'balance' => number_format($balance * -1, 2, '.', ''), 'credit' => $credit, 'active' => $active];
'credit' => $credit, 'active' => $active];
} }
return $result; return $result;
} }
...@@ -1049,7 +1048,7 @@ class Invoice extends Base { ...@@ -1049,7 +1048,7 @@ class Invoice extends Base {
} }
$message .= '<tr><td>' . htmlspecialchars($name) . '</td><td>' . $message .= '<tr><td>' . htmlspecialchars($name) . '</td><td>' .
$supplier . '</td><td>' . $next_week[$i]['quantity'] . '</td><td>$' . $supplier . '</td><td>' . $next_week[$i]['quantity'] . '</td><td>$' .
number_format($next_week[$i]['price'], 2, '.', '') . '/' . price_string($next_week[$i]['price']) . '/' .
$products[$name]['unit'] . '</td></tr>' . "\n"; $products[$name]['unit'] . '</td></tr>' . "\n";
} }
$message .= "</table>\n" . $message .= "</table>\n" .
...@@ -1307,22 +1306,19 @@ class Invoice extends Base { ...@@ -1307,22 +1306,19 @@ class Invoice extends Base {
$surcharge_description = $surcharge_description =
$this->Substitute('invoice-surcharge-description'); $this->Substitute('invoice-surcharge-description');
$message .= '<tr><td></td><td></td><td></td><td>Subtotal</td><td>$' . $message .= '<tr><td></td><td></td><td></td><td>Subtotal</td><td>$' .
number_format($total, 2, '.', '') . '</td></tr>' . "\n" . price_string($total) . '</td></tr>' . "\n" .
'<tr><td></td><td></td><td></td><td>' . $surcharge_description . '<tr><td></td><td></td><td></td><td>' . $surcharge_description .
'</td><td>$' . number_format($surcharge, 2, '.', '') . '</td></tr>' . '</td><td>$' . price_string($surcharge) . '</td></tr>' . "\n" .
"\n" .
'<tr><td></td><td></td><td></td><td><b>Total</b></td><td><b>$' . '<tr><td></td><td></td><td></td><td><b>Total</b></td><td><b>$' .
number_format($total + $surcharge, 2, '.', '') . price_string($total + $surcharge) . '</b></td></tr></table>' . "\n";
'</b></td></tr></table>' . "\n";
} }
else { else {
$message .= '<tr><td></td><td></td><td></td><td><b>Total</b></td><td>' . $message .= '<tr><td></td><td></td><td></td><td><b>Total</b></td><td>' .
'<b>$' . number_format($total, 2, '.', '') . '</b></td></tr>' . '<b>$' . price_string($total) . '</b></td></tr></table>' . "\n";
'</table>' . "\n";
} }
if ($tax_included !== 0) { if ($tax_included !== 0) {
$message .= $this->Substitute('invoice-tax-included', '/!total/', $message .= $this->Substitute('invoice-tax-included', '/!total/',
number_format($tax_included, 2, '.', '')); price_string($tax_included));
} }
} }
if ($xero_enabled && $invoice_number !== 0) { if ($xero_enabled && $invoice_number !== 0) {
...@@ -1342,9 +1338,9 @@ class Invoice extends Base { ...@@ -1342,9 +1338,9 @@ class Invoice extends Base {
'<DueDate>' . date('Y-m-d\TH:i:s', $invoice_date + $terms) . '<DueDate>' . date('Y-m-d\TH:i:s', $invoice_date + $terms) .
'</DueDate>' . '</DueDate>' .
'<InvoiceNumber>' . $invoice_number . '</InvoiceNumber>' . '<InvoiceNumber>' . $invoice_number . '</InvoiceNumber>' .
'<TotalTax>' . number_format($tax_included, 2, '.', '') . '<TotalTax>' . price_string($tax_included) .
'</TotalTax>' . '</TotalTax>' .
'<Total>' . number_format($total + $surcharge, 2, '.', '') . '<Total>' . price_string($total + $surcharge) .
'</Total>' . '</Total>' .
'<LineAmountTypes>Inclusive</LineAmountTypes>' . '<LineAmountTypes>Inclusive</LineAmountTypes>' .
'<LineItems>' . $line_items . '</LineItems>' . '<LineItems>' . $line_items . '</LineItems>' .
...@@ -1397,7 +1393,7 @@ class Invoice extends Base { ...@@ -1397,7 +1393,7 @@ class Invoice extends Base {
// Only show the title if there's more than one product sold. // Only show the title if there's more than one product sold.
if (count($data) > 1) { if (count($data) > 1) {
$message .= '<tr><td></td><td></td><td><b>Total</b></td><td><b>$' . $message .= '<tr><td></td><td></td><td><b>Total</b></td><td><b>$' .
number_format($total, 2, '.', '') . '</b></td></tr>'; price_string($total) . '</b></td></tr>';
} }
$message .= '</table>' . "\n"; $message .= '</table>' . "\n";
} }
...@@ -1474,14 +1470,14 @@ class Invoice extends Base { ...@@ -1474,14 +1470,14 @@ class Invoice extends Base {
$invoice_balance = (float)$this->Substitute('invoice-balance'); $invoice_balance = (float)$this->Substitute('invoice-balance');
if ($balance > $invoice_balance + 0.01) { if ($balance > $invoice_balance + 0.01) {
if ($credit === 1) { if ($credit === 1) {
$text .= '<p>Your balance is currently <b>$' . $text .= '<p>Your balance is currently <b>$' . price_string($balance) .
number_format($balance, 2, '.', '') . '</b> and you have chosen ' . '</b> and you have chosen to keep this as credit in the system.</p>' .
'to keep this as credit in the system.</p>' . "\n"; "\n";
} }
else { else {
$text .= '<p>Your balance is currently <b>$' . $text .= '<p>Your balance is currently <b>$' . price_string($balance) .
number_format($balance, 2, '.', '') . '</b>, ' . '</b>, please expect a payment from our finance team shortly.</p>' .
'please expect a payment from our finance team shortly.</p>' . "\n"; "\n";
} }
} }
else if ($balance < $invoice_balance - 0.01) { else if ($balance < $invoice_balance - 0.01) {
...@@ -1497,12 +1493,12 @@ class Invoice extends Base { ...@@ -1497,12 +1493,12 @@ class Invoice extends Base {
// Also if processing has never been done, don't want to show a timestamp. // Also if processing has never been done, don't want to show a timestamp.
if ($timestamp === 0 || time() < $timestamp + 86400) { if ($timestamp === 0 || time() < $timestamp + 86400) {
$text .= $this->Substitute('invoice-current-balance', '/!balance/', $text .= $this->Substitute('invoice-current-balance', '/!balance/',
number_format($balance, 2, '.', '')); price_string($balance));
} }
else { else {
$text .= $this->Substitute('invoice-old-balance', $text .= $this->Substitute('invoice-old-balance',
['/!balance/', '/!date/'], ['/!balance/', '/!date/'],
[number_format($balance, 2, '.', ''), [price_string($balance),
date('j F Y', $timestamp)]); date('j F Y', $timestamp)]);
} }
} }
...@@ -1612,7 +1608,7 @@ class Invoice extends Base { ...@@ -1612,7 +1608,7 @@ class Invoice extends Base {
$quantity = $details['quantity']; $quantity = $details['quantity'];
$unit = isset($products[$name]['unit']) ? $unit = isset($products[$name]['unit']) ?
'/' . $products[$name]['unit'] : ''; '/' . $products[$name]['unit'] : '';
$price = '$' . number_format($details['basePrice'], 2, '.', '') . $unit; $price = '$' . price_string($details['basePrice']) . $unit;
if ($format === 'user') { if ($format === 'user') {
$message .= '<tr><td>' . $user . '</td><td>' . $name . '</td><td>' . $message .= '<tr><td>' . $user . '</td><td>' . $name . '</td><td>' .
$supplier . '</td><td>' . $quantity . '</td><td>' . $price . $supplier . '</td><td>' . $quantity . '</td><td>' . $price .
...@@ -1743,7 +1739,7 @@ class Invoice extends Base { ...@@ -1743,7 +1739,7 @@ class Invoice extends Base {
$quantity = $details['quantity']; $quantity = $details['quantity'];
$unit = isset($products[$name]['unit']) ? $unit = isset($products[$name]['unit']) ?
'/' . $products[$name]['unit'] : ''; '/' . $products[$name]['unit'] : '';
$price = '$' . number_format($details['basePrice'], 2, '.', '') . $unit; $price = '$' . price_string($details['basePrice']) . $unit;
$previous_name = $name; $previous_name = $name;
$total_quantity += $quantity; $total_quantity += $quantity;
$count++; $count++;
...@@ -1760,7 +1756,7 @@ class Invoice extends Base { ...@@ -1760,7 +1756,7 @@ class Invoice extends Base {
} }
// Also store each product order in the supplier data. // Also store each product order in the supplier data.
if (!is_array($data[$supplier])) $data[$supplier] = []; if (!isset($data[$supplier])) $data[$supplier] = [];
if ($format === 'vertical') { if ($format === 'vertical') {
$size = isset($products[$name]['size']) ? $size = isset($products[$name]['size']) ?
$products[$name]['size'] : 'unavailable'; $products[$name]['size'] : 'unavailable';
...@@ -1769,7 +1765,7 @@ class Invoice extends Base { ...@@ -1769,7 +1765,7 @@ class Invoice extends Base {
'size' => $size]; 'size' => $size];
} }
else { else {
if (!is_array($data[$supplier][$name])) { if (!isset($data[$supplier][$name])) {
$size = isset($products[$name]['size']) ? $size = isset($products[$name]['size']) ?
$products[$name]['size'] : 'unavailable'; $products[$name]['size'] : 'unavailable';
$data[$supplier][$name] = ['price' => $price, 'group' => [], $data[$supplier][$name] = ['price' => $price, 'group' => [],
...@@ -2225,7 +2221,7 @@ class Invoice extends Base { ...@@ -2225,7 +2221,7 @@ class Invoice extends Base {
'payments' => '']; 'payments' => ''];
$balance = $total_payments + $total_sold - $total_purchase; $balance = $total_payments + $total_sold - $total_purchase;
$indexed[] = ['date' => 'BALANCE', 'purchases' => '', 'sold' => '', $indexed[] = ['date' => 'BALANCE', 'purchases' => '', 'sold' => '',
'payments' => number_format($balance, 2, '.', '')]; 'payments' => price_string($balance)];
$date = date('Y-m-d'); $date = date('Y-m-d');
$filename = 'invoice-' . $username . '-' . $date . '.csv'; $filename = 'invoice-' . $username . '-' . $date . '.csv';
$this->CreateCSV($filename, $indexed, false); $this->CreateCSV($filename, $indexed, false);
......
This diff is collapsed.
...@@ -438,8 +438,9 @@ class Stock extends Base { ...@@ -438,8 +438,9 @@ class Stock extends Base {
'system_group = "' . $group . '" AND cart = 1'; 'system_group = "' . $group . '" AND cart = 1';
if ($mysqli_result = $mysqli->query($query)) { if ($mysqli_result = $mysqli->query($query)) {
while ($stock_order_price = $mysqli_result->fetch_assoc()) { while ($stock_order_price = $mysqli_result->fetch_assoc()) {
$this->UpdateCart($stock_order_price['name'], $name = $mysqli->escape_string($stock_order_price['name']);
$stock_order_price['user']); $user = $mysqli->escape_string($stock_order_price['user']);
$this->UpdateCart($name, $user);
} }
$mysqli_result->close(); $mysqli_result->close();
} }
......
This diff is collapsed.
...@@ -77,7 +77,7 @@ var formData=new FormData();if(!formData){dobrado.log('Your browser doesn\'t sup ...@@ -77,7 +77,7 @@ var formData=new FormData();if(!formData){dobrado.log('Your browser doesn\'t sup
dobrado.log('Importing file...','info');var file=$('#reader-file-import').get(0).files[0];if(file){formData.append('file',file);formData.append('request','reader');formData.append('action','import');formData.append('url',location.href);formData.append('token',dobrado.token);$.ajax({url:'/php/request.php',data:formData,contentType:false,processData:false,type:'POST',success:function(response){if(dobrado.checkResponseError(response,'reader importFile')){return;} dobrado.log('Importing file...','info');var file=$('#reader-file-import').get(0).files[0];if(file){formData.append('file',file);formData.append('request','reader');formData.append('action','import');formData.append('url',location.href);formData.append('token',dobrado.token);$.ajax({url:'/php/request.php',data:formData,contentType:false,processData:false,type:'POST',success:function(response){if(dobrado.checkResponseError(response,'reader importFile')){return;}
importList=JSON.parse(response);importCount=0;if(importList.length!==0){var text='<p>Found <b>'+importList.length+'</b> feeds in imported file:</p>';$('.reader-discovered').html(text).dialog('open');addImportedFeed();}}});} importList=JSON.parse(response);importCount=0;if(importList.length!==0){var text='<p>Found <b>'+importList.length+'</b> feeds in imported file:</p>';$('.reader-discovered').html(text).dialog('open');addImportedFeed();}}});}
return false;} return false;}
function newFeedEvents(){(function tooltipEvents(){var isTooltipOpen=false;var channelOptions=$('#reader-channel-select').html();function tooltipContent(){var name=$(this).html();var link=$(this).attr('href');var shortLink=link;if(shortLink.length>50){shortLink=shortLink.substring(0,50)+'...';} function newFeedEvents(){(function tooltipEvents(){var tooltipClick=false;var channelOptions=$('#reader-channel-select').html();function tooltipContent(){var name=$(this).html();var link=$(this).attr('href');var shortLink=link;if(shortLink.length>50){shortLink=shortLink.substring(0,50)+'...';}
var feed=$(this).parents('.reader-group').find('a.reader-group-link');var feedUrl=feed.attr('href');var photo=$(this).siblings('.author-photo-url');var content='';if(photo.length!==0){content+='<a class="tooltip-photo" href="'+ var feed=$(this).parents('.reader-group').find('a.reader-group-link');var feedUrl=feed.attr('href');var photo=$(this).siblings('.author-photo-url');var content='';if(photo.length!==0){content+='<a class="tooltip-photo" href="'+
photo.attr('href')+'">'+photo.html()+'</a>';} photo.attr('href')+'">'+photo.html()+'</a>';}
content+='<a class="tooltip-name" href="'+link+'">'+ content+='<a class="tooltip-name" href="'+link+'">'+
...@@ -87,24 +87,24 @@ feedUrl+'"></a>';} ...@@ -87,24 +87,24 @@ feedUrl+'"></a>';}
else{content+='Channel for posts from '+name+' in <a class="tooltip-feed" href="'+feedUrl+'">'+ else{content+='Channel for posts from '+name+' in <a class="tooltip-feed" href="'+feedUrl+'">'+
feed.html()+'</a>: ';} feed.html()+'</a>: ';}
content+='<select class="tooltip-channel-select">'+ content+='<select class="tooltip-channel-select">'+
channelOptions+'<option>Add new channel</option></select></div>'+'<div class="hidden">'+'<label>Add channel:</label>'+'<input class="tooltip-channel-add" type="text">'+'<button class="tooltip-channel-submit">submit</button>'+'</div>';} channelOptions+'<option>Add channel</option></select></div>'+'<div class="hidden">'+'<label>Add channel:</label>'+'<input class="tooltip-channel-add" type="text">'+'<button class="tooltip-channel-submit">submit</button>'+'</div>';}
return content;} return content;}
function tooltipOpen(event,ui){function tooltipAddChannel(){var option=$('#'+tooltipId+' .tooltip-channel-add').val();if(option===''){return;} function tooltipOpen(event,ui){function tooltipAddChannel(){var option=$('#'+tooltipId+' .tooltip-channel-add').val();if(option===''){return;}
$('#'+tooltipId+' .tooltip-channel-add').val('');tooltipSetChannel(option,true);} $('#'+tooltipId+' .tooltip-channel-add').val('');tooltipSetChannel(option,true);}
function tooltipSelectChannel(event,ui){if(ui.item.value==='Add new channel'){$('#'+tooltipId+' .tooltip-channel-add').parent().show();} function tooltipSelectChannel(event,ui){if(ui.item.value==='Add channel'){$('#'+tooltipId+' .tooltip-channel-add').parent().show();}
else{tooltipSetChannel(ui.item.value,false);}} else{tooltipSetChannel(ui.item.value,false);}}
function tooltipSetChannel(option,add){var feed=$('#'+tooltipId+' .tooltip-feed').attr('href');var author=$('#'+tooltipId+' .tooltip-name').html();dobrado.log('Setting channel for author','info');$.post('/php/request.php',{id:'#'+$('.reader').attr('id'),request:'reader',action:'setChannel',channel:option,feed:feed,author:author,url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,'tooltipsetChannel')){return;} function tooltipSetChannel(option,add){var feed=$('#'+tooltipId+' .tooltip-feed').attr('href');var author=$('#'+tooltipId+' .tooltip-name').html();dobrado.log('Setting channel for author','info');$.post('/php/request.php',{id:'#'+$('.reader').attr('id'),request:'reader',action:'setChannel',channel:option,feed:feed,author:author,url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,'tooltipsetChannel')){return;}
var channel=JSON.parse(response);if(add){$('#reader-channel-select').append('<option>'+channel.name+'</option>');} var channel=JSON.parse(response);if(add){$('#reader-channel-select').append('<option>'+channel.name+'</option>');}
if(dobrado.localStorage){let authorChannel={};if(localStorage.readerAuthorChannel){authorChannel=JSON.parse(localStorage.readerAuthorChannel);} $('.ui-tooltip').remove();if(dobrado.localStorage){let authorChannel={};if(localStorage.readerAuthorChannel){authorChannel=JSON.parse(localStorage.readerAuthorChannel);}
authorChannel[author+' '+feed]=channel.name;localStorage.readerAuthorChannel=JSON.stringify(authorChannel);}});} authorChannel[author+' '+feed]=channel.name;localStorage.readerAuthorChannel=JSON.stringify(authorChannel);}});}
var tooltipId=ui.tooltip.attr('id');isTooltipOpen=true;$('#'+tooltipId+' .tooltip-channel-submit').button().click(tooltipAddChannel);let author=$('#'+tooltipId+' .tooltip-name').html();let feed=$('#'+tooltipId+' .tooltip-feed').attr('href');let currentChannel=$('#page-select').val();if(dobrado.localStorage&&localStorage.readerAuthorChannel){let authorChannel=JSON.parse(localStorage.readerAuthorChannel);if(authorChannel[author+' '+feed]){currentChannel=authorChannel[author+' '+feed];} var tooltipId=ui.tooltip.attr('id');$('#'+tooltipId+' .tooltip-channel-submit').button().click(tooltipAddChannel);let author=$('#'+tooltipId+' .tooltip-name').html();let feed=$('#'+tooltipId+' .tooltip-feed').attr('href');let currentChannel=$('#page-select').val();if(dobrado.localStorage&&localStorage.readerAuthorChannel){let authorChannel=JSON.parse(localStorage.readerAuthorChannel);if(authorChannel[author+' '+feed]){currentChannel=authorChannel[author+' '+feed];}
else if(authorChannel[feed]){currentChannel=authorChannel[feed];}} else if(authorChannel[feed]){currentChannel=authorChannel[feed];}}
$('#'+tooltipId+' .tooltip-channel-select').val(currentChannel).selectmenu({change:tooltipSelectChannel});} $('#'+tooltipId+' .tooltip-channel-select').val(currentChannel).selectmenu({change:tooltipSelectChannel});}
function tooltipClose(event,ui){function over(){$(this).stop(true).fadeIn();} function tooltipClose(event,ui){function over(){$(this).stop(true).fadeIn();}
function out(){var selectmenu='#'+tooltipId+' .ui-selectmenu-button';if(!$(selectmenu).hasClass('ui-selectmenu-button-open')){$(this).fadeOut(400,function(){$(this).remove();});}} function out(){var selectmenu='#'+tooltipId+' .ui-selectmenu-button';if(!$(selectmenu).hasClass('ui-selectmenu-button-open')){$(this).fadeOut(400,function(){$(this).remove();});}}
var tooltipId=ui.tooltip.attr('id');ui.tooltip.hover(over,out);isTooltipOpen=false;} var tooltipId=ui.tooltip.attr('id');ui.tooltip.hover(over,out);tooltipClick=false;}
if(localStorage.readerChannelInfo){$('.reader-item .author-name').tooltip({items:'.author-name',content:tooltipContent,open:tooltipOpen,close:tooltipClose}).click(function(){if(isTooltipOpen){$(this).tooltip('close');} if(localStorage.readerChannelInfo){$('.reader-item .author-name').tooltip({items:'.author-name',content:tooltipContent,open:tooltipOpen,close:tooltipClose}).click(function(){tooltipClick=!tooltipClick;if(tooltipClick){$(this).tooltip('open');}
else{$(this).tooltip('open');} else{$(this).tooltip('close');}
return false;});$('.reader-item .author-name').tooltip('close');}}());$('.reader-actions a').click(readerAction);$('.reader-content .read-more').click(function(){$(this).parent().hide().siblings('.real-content').show();return false;});$('.reader-content .show-group').click(function(){$(this).siblings('.reader-item').show();$(this).hide();return false;});dobrado.indieConfig();} return false;});$('.reader-item .author-name').tooltip('close');}}());$('.reader-actions a').click(readerAction);$('.reader-content .read-more').click(function(){$(this).parent().hide().siblings('.real-content').show();return false;});$('.reader-content .show-group').click(function(){$(this).siblings('.reader-item').show();$(this).hide();return false;});dobrado.indieConfig();}
function readerAction(){var content='';var sendTo='';var item=$(this).parents('.reader-item');var permalink=item.find('.permalink').attr('href');if(!dobrado.editor){dobrado.writer.showEditor();} function readerAction(){var content='';var sendTo='';var item=$(this).parents('.reader-item');var permalink=item.find('.permalink').attr('href');if(!dobrado.editor){dobrado.writer.showEditor();}
if(item.hasClass('twitter-atom-appspot-com')){sendTo='twitter';} if(item.hasClass('twitter-atom-appspot-com')){sendTo='twitter';}
...@@ -157,9 +157,11 @@ if(notify){dobrado.notify('feed',dobrado.reader.update);}});} ...@@ -157,9 +157,11 @@ if(notify){dobrado.notify('feed',dobrado.reader.update);}});}
function showHidden(){$('.reader-hidden').show();$('.reader-show-hidden-wrapper').hide();let currentChannel=$('#page-select').val();if(currentChannel==='all'){$('#page-select > option').each(function(){var value=$(this).val();if(value!=='all'){$(this).html(value);$(this).attr('data-class','false');}});} function showHidden(){$('.reader-hidden').show();$('.reader-show-hidden-wrapper').hide();let currentChannel=$('#page-select').val();if(currentChannel==='all'){$('#page-select > option').each(function(){var value=$(this).val();if(value!=='all'){$(this).html(value);$(this).attr('data-class','false');}});}
else{updatePageSelect(currentChannel,false);} else{updatePageSelect(currentChannel,false);}
if(dobrado.control){$('#page-select').pageselectmenu('refresh');}} if(dobrado.control){$('#page-select').pageselectmenu('refresh');}}
function updatePageSelect(channel,unread){$('#page-select > option').each(function(){if($(this).val()===channel){if(unread===false||unread===0){$(this).html(channel);$(this).attr('data-class','false');} function updatePageSelect(channel,unread){$('#page-select > option').each(function(){if($(this).val()===channel){if(unread===false){$(this).html(channel);$(this).attr('data-class','false');}
else if(unread===true){$(this).html(channel);$(this).attr('data-class','true');} else if(unread===true){$(this).html(channel);$(this).attr('data-class','true');}
else{$(this).html(channel+' ('+unread+')');$(this).attr('data-class','true');} else{let currentUnread=0;let fields=$(this).html().match(/\(([0-9]+)\)$/);if(fields&&fields.length===2){currentUnread=parseInt(fields[1],10);}
unread+=currentUnread;if(currentUnread===0){$(this).html(channel);$(this).attr('data-class','false');}
else{$(this).html(channel+' ('+unread+')');$(this).attr('data-class','true');}}
return false;}});} return false;}});}
dobrado.reader.addFeed=function(xmlUrl,force,multiple){function addDiscoveredFeed(){var discovered=[];$('.reader-discovered input:checked').each(function(index){discovered.push($(this).val());});if(discovered.length===1){dobrado.reader.addFeed(discovered[0],true);}