diff --git a/public/viewjs/transfer.js b/public/viewjs/transfer.js
index 9bfc8116..f6fdcb66 100644
--- a/public/viewjs/transfer.js
+++ b/public/viewjs/transfer.js
@@ -36,6 +36,8 @@ $('#save-transfer-button').on('click', function(e)
jsonData.stock_entry_id = jsonForm.specific_stock_entry;
}
+ var bookingResponse = null;
+
Grocy.Api.Get('stock/products/' + jsonForm.product_id,
function(productDetails)
{
@@ -43,6 +45,8 @@ $('#save-transfer-button').on('click', function(e)
function(result)
{
var addBarcode = GetUriParam('addbarcodetoselection');
+ bookingResponse = result;
+
if (addBarcode !== undefined)
{
var existingBarcodes = productDetails.product.barcode || '';
@@ -71,11 +75,11 @@ $('#save-transfer-button').on('click', function(e)
if (productDetails.product.enable_tare_weight_handling == 1)
{
- var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount - parseFloat(productDetails.product.tare_weight)) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name,$('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text());
+ var successMessage = __t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount - parseFloat(productDetails.product.tare_weight)) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name,$('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '
' + __t("Undo") + '';
}
else
{
- var successMessage =__t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text());
+ var successMessage =__t('Transfered %1$s of %2$s from %3$s to %4$s', Math.abs(jsonForm.amount) + " " + __n(jsonForm.amount, productDetails.quantity_unit_stock.name, productDetails.quantity_unit_stock.name_plural), productDetails.product.name, $('option:selected', "#location_id_from").text(), $('option:selected', "#location_id_to").text()) + '
' + __t("Undo") + '';
}
if (GetUriParam("embedded") !== undefined)
@@ -407,3 +411,31 @@ $("#use_specific_stock_entry").on("change", function()
Grocy.FrontendHelpers.ValidateForm("transfer-form");
});
+
+function UndoStockBooking(bookingId)
+{
+ Grocy.Api.Post('stock/bookings/' + bookingId.toString() + '/undo', { },
+ function(result)
+ {
+ toastr.success(__t("Booking successfully undone"));
+ },
+ function(xhr)
+ {
+ console.error(xhr);
+ }
+ );
+};
+
+function UndoStockTransaction(transactionId)
+{
+ Grocy.Api.Post('stock/transactions/' + transactionId.toString() + '/undo', { },
+ function (result)
+ {
+ toastr.success(__t("Transaction successfully undone"));
+ },
+ function (xhr)
+ {
+ console.error(xhr);
+ }
+ );
+};
diff --git a/services/StockService.php b/services/StockService.php
index 08d7a6c0..2e42dd20 100644
--- a/services/StockService.php
+++ b/services/StockService.php
@@ -930,6 +930,7 @@ class StockService extends BaseService
{
$this->UndoBooking($correlatedBooking->id, true);
}
+ return;
}
$hasSubsequentBookings = $this->Database->stock_log()->where('stock_id = :1 AND id != :2 AND (correlation_id is not null OR correlation_id != :3) AND id > :2 AND undone = 0', $logRow->stock_id, $logRow->id, $logRow->correlation_id)->count() > 0;
@@ -970,6 +971,60 @@ class StockService extends BaseService
'undone_timestamp' => date('Y-m-d H:i:s')
));
}
+ elseif ($logRow->transaction_type === self::TRANSACTION_TYPE_TRANSFER_TO)
+ {
+ $stockRow = $this->Database->stock()->where('stock_id = :1 AND location_id = :2', $logRow->stock_id, $logRow->location_id)->fetch();
+ if ($stockRow === null)
+ {
+ throw new \Exception('Booking does not exist or was already undone');
+ }
+ $newAmount = $stockRow->amount - $logRow->amount;
+
+ if ($newAmount == 0)
+ {
+ $stockRow->delete();
+ } else {
+ // Remove corresponding amount back to stock
+ $stockRow->update(array(
+ 'amount' => $newAmount
+ ));
+ }
+
+ // Update log entry
+ $logRow->update(array(
+ 'undone' => 1,
+ 'undone_timestamp' => date('Y-m-d H:i:s')
+ ));
+ }
+ elseif ($logRow->transaction_type === self::TRANSACTION_TYPE_TRANSFER_FROM)
+ {
+ // Add corresponding amount back to stock or
+ // create a row if missing
+ $stockRow = $this->Database->stock()->where('stock_id = :1 AND location_id = :2', $logRow->stock_id, $logRow->location_id)->fetch();
+ if ($stockRow === null)
+ {
+ $stockRow = $this->Database->stock()->createRow(array(
+ 'product_id' => $logRow->product_id,
+ 'amount' => $logRow->amount * -1,
+ 'best_before_date' => $logRow->best_before_date,
+ 'purchased_date' => $logRow->purchased_date,
+ 'stock_id' => $logRow->stock_id,
+ 'price' => $logRow->price,
+ 'opened_date' => $logRow->opened_date
+ ));
+ $stockRow->save();
+ } else {
+ $stockRow->update(array(
+ 'amount' => $stockRow->amount - $logRow->amount
+ ));
+ }
+
+ // Update log entry
+ $logRow->update(array(
+ 'undone' => 1,
+ 'undone_timestamp' => date('Y-m-d H:i:s')
+ ));
+ }
elseif ($logRow->transaction_type === self::TRANSACTION_TYPE_PRODUCT_OPENED)
{
// Remove opened flag from corresponding log entry