Stock-Journal: API, Summary, Done By (#989)

* Stockjournal: Add "Done by"

* Add API for Stock-Journal

* Add "Journal-Summary"

* Use ALTER TABLE

* Moved the "Jounral summary" button to the stock journal page

* Changed icon & context menu position for new stock journal summary page

Co-authored-by: Bernd Bestel <bernd@berrnd.de>
This commit is contained in:
fipwmaqzufheoxq92ebc 2020-09-06 13:18:51 +02:00 committed by GitHub
parent 7498d8f13d
commit 0454c128f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 183 additions and 23 deletions

View File

@ -715,6 +715,16 @@ class StockApiController extends BaseApiController
} }
} }
public function Journal(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{
return $this->FilteredApiResponse($response, $this->getDatabase()->uihelper_stock_journal(), $request->getQueryParams());
}
public function JournalSummary(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{
return $this->FilteredApiResponse($response, $this->getDatabase()->uihelper_stock_journal_summary(), $request->getQueryParams());
}
public function __construct(\DI\Container $container) public function __construct(\DI\Container $container)
{ {
parent::__construct($container); parent::__construct($container);

View File

@ -33,10 +33,8 @@ class StockController extends BaseController
public function Journal(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) public function Journal(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{ {
return $this->renderPage($response, 'stockjournal', [ return $this->renderPage($response, 'stockjournal', [
'stockLog' => $this->getDatabase()->stock_log()->orderBy('row_created_timestamp', 'DESC'), 'stockLog' => $this->getDatabase()->uihelper_stock_journal()->orderBy('row_created_timestamp', 'DESC'),
'locations' => $this->getDatabase()->locations()->orderBy('name'),
'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'), 'products' => $this->getDatabase()->products()->where('active = 1')->orderBy('name'),
'quantityunits' => $this->getDatabase()->quantity_units()->orderBy('name')
]); ]);
} }
@ -442,4 +440,24 @@ class StockController extends BaseController
{ {
parent::__construct($container); parent::__construct($container);
} }
public function JournalSummary(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
{
$entries = $this->getDatabase()->uihelper_stock_journal_summary();
if (isset($request->getQueryParams()['product_id']))
{
$entries = $entries->where('product_id', $request->getQueryParams()['product_id']);
}
if (isset($request->getQueryParams()['user_id']))
{
$entries = $entries->where('user_id', $request->getQueryParams()['user_id']);
}
if (isset($request->getQueryParams()['transaction_type']))
{
$entries = $entries->where('transaction_type', $request->getQueryParams()['transaction_type']);
}
return $this->renderPage($response, 'stockjournalsummary', [
'entries' => $entries
]);
}
} }

View File

@ -1900,3 +1900,12 @@ msgstr ""
msgid "Default" msgid "Default"
msgstr "" msgstr ""
msgid "Stock journal summary"
msgstr ""
msgid "Journal summary"
msgstr ""
msgid "Journal summary for this product"
msgstr ""

41
migrations/0115.sql Normal file
View File

@ -0,0 +1,41 @@
ALTER TABLE stock_log ADD COLUMN
user_id INTEGER NOT NULL DEFAULT 1;
CREATE VIEW uihelper_stock_journal
AS
SELECT stock_log.id,
stock_log.row_created_timestamp,
stock_log.correlation_id,
stock_log.undone,
stock_log.undone_timestamp,
stock_log.row_created_timestamp,
stock_log.transaction_type,
stock_log.spoiled,
stock_log.amount,
stock_log.location_id,
l.name AS location_name,
p.name AS product_name,
qu.name AS qu_name,
qu.name_plural AS qu_name_plural,
u.display_name AS user_display_name
FROM stock_log
JOIN users_dto u on stock_log.user_id = u.id
JOIN products p on stock_log.product_id = p.id
JOIN locations l on p.location_id = l.id
JOIN quantity_units qu ON p.qu_id_stock = qu.id;
CREATE VIEW uihelper_stock_journal_summary
AS
SELECT user_id AS id, -- dummy, LessQL needs an id column
user_id, u.display_name AS user_display_name, p.name AS product_name, product_id, transaction_type,
qu.name AS qu_name,
qu.name_plural AS qu_name_plural,
SUM(amount) AS amount
FROM stock_log
JOIN users_dto u on stock_log.user_id = u.id
JOIN products p on stock_log.product_id = p.id
JOIN quantity_units qu ON p.qu_id_stock = qu.id
WHERE undone = 0
GROUP BY user_id, product_id,transaction_type;

View File

@ -0,0 +1,6 @@
var journalSummaryTable = $('#journal-summary-table').DataTable({
'paginate': true,
'order': [[0, 'desc']]
});
$('#journal-summary-table tbody').removeClass("d-none");
journalSummaryTable.columns.adjust().draw();

View File

@ -57,6 +57,7 @@ $app->group('', function (RouteCollectorProxy $group) {
$group->get('/stockjournal', '\Grocy\Controllers\StockController:Journal'); $group->get('/stockjournal', '\Grocy\Controllers\StockController:Journal');
$group->get('/locationcontentsheet', '\Grocy\Controllers\StockController:LocationContentSheet'); $group->get('/locationcontentsheet', '\Grocy\Controllers\StockController:LocationContentSheet');
$group->get('/quantityunitpluraltesting', '\Grocy\Controllers\StockController:QuantityUnitPluralFormTesting'); $group->get('/quantityunitpluraltesting', '\Grocy\Controllers\StockController:QuantityUnitPluralFormTesting');
$group->get('/stockjournal/summary', '\Grocy\Controllers\StockController:JournalSummary');
} }
// Stock price tracking // Stock price tracking
@ -203,6 +204,8 @@ $app->group('/api', function (RouteCollectorProxy $group) {
$group->post('/stock/transactions/{transactionId}/undo', '\Grocy\Controllers\StockApiController:UndoTransaction'); $group->post('/stock/transactions/{transactionId}/undo', '\Grocy\Controllers\StockApiController:UndoTransaction');
$group->get('/stock/barcodes/external-lookup/{barcode}', '\Grocy\Controllers\StockApiController:ExternalBarcodeLookup'); $group->get('/stock/barcodes/external-lookup/{barcode}', '\Grocy\Controllers\StockApiController:ExternalBarcodeLookup');
$group->get('/productbarcodedetails/{barcode}', '\Grocy\Controllers\StockApiController:ProductBarcodeDetails'); $group->get('/productbarcodedetails/{barcode}', '\Grocy\Controllers\StockApiController:ProductBarcodeDetails');
$group->get('/stock/journal', '\Grocy\Controllers\StockApiController:Journal');
$group->get('/stock/journal/summary', '\Grocy\Controllers\StockApiController:JournalSummary');
} }
// Shopping list // Shopping list

View File

@ -112,7 +112,8 @@ class StockService extends BaseService
'location_id' => $locationId, 'location_id' => $locationId,
'transaction_id' => $transactionId, 'transaction_id' => $transactionId,
'shopping_location_id' => $shoppingLocationId, 'shopping_location_id' => $shoppingLocationId,
'qu_factor_purchase_to_stock' => $quFactorPurchaseToStock 'qu_factor_purchase_to_stock' => $quFactorPurchaseToStock,
'user_id' => GROCY_USER_ID
]); ]);
$logRow->save(); $logRow->save();
@ -259,7 +260,8 @@ class StockService extends BaseService
'price' => $stockEntry->price, 'price' => $stockEntry->price,
'opened_date' => $stockEntry->opened_date, 'opened_date' => $stockEntry->opened_date,
'recipe_id' => $recipeId, 'recipe_id' => $recipeId,
'transaction_id' => $transactionId 'transaction_id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRow->save(); $logRow->save();
@ -283,7 +285,8 @@ class StockService extends BaseService
'price' => $stockEntry->price, 'price' => $stockEntry->price,
'opened_date' => $stockEntry->opened_date, 'opened_date' => $stockEntry->opened_date,
'recipe_id' => $recipeId, 'recipe_id' => $recipeId,
'transaction_id' => $transactionId 'transaction_id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRow->save(); $logRow->save();
@ -328,7 +331,8 @@ class StockService extends BaseService
'qu_factor_purchase_to_stock' => $stockRow->qu_factor_purchase_to_stock, 'qu_factor_purchase_to_stock' => $stockRow->qu_factor_purchase_to_stock,
'correlation_id' => $correlationId, 'correlation_id' => $correlationId,
'transaction_id' => $transactionId, 'transaction_id' => $transactionId,
'stock_row_id' => $stockRow->id 'stock_row_id' => $stockRow->id,
'user_id' => GROCY_USER_ID
]); ]);
$logOldRowForStockUpdate->save(); $logOldRowForStockUpdate->save();
@ -369,7 +373,8 @@ class StockService extends BaseService
'qu_factor_purchase_to_stock' => $stockRow->qu_factor_purchase_to_stock, 'qu_factor_purchase_to_stock' => $stockRow->qu_factor_purchase_to_stock,
'correlation_id' => $correlationId, 'correlation_id' => $correlationId,
'transaction_id' => $transactionId, 'transaction_id' => $transactionId,
'stock_row_id' => $stockRow->id 'stock_row_id' => $stockRow->id,
'user_id' => GROCY_USER_ID
]); ]);
$logNewRowForStockUpdate->save(); $logNewRowForStockUpdate->save();
@ -738,7 +743,8 @@ class StockService extends BaseService
'transaction_type' => self::TRANSACTION_TYPE_PRODUCT_OPENED, 'transaction_type' => self::TRANSACTION_TYPE_PRODUCT_OPENED,
'price' => $stockEntry->price, 'price' => $stockEntry->price,
'opened_date' => date('Y-m-d'), 'opened_date' => date('Y-m-d'),
'transaction_id' => $transactionId 'transaction_id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRow->save(); $logRow->save();
@ -777,7 +783,8 @@ class StockService extends BaseService
'transaction_type' => self::TRANSACTION_TYPE_PRODUCT_OPENED, 'transaction_type' => self::TRANSACTION_TYPE_PRODUCT_OPENED,
'price' => $stockEntry->price, 'price' => $stockEntry->price,
'opened_date' => date('Y-m-d'), 'opened_date' => date('Y-m-d'),
'transaction_id' => $transactionId 'transaction_id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRow->save(); $logRow->save();
@ -914,7 +921,8 @@ class StockService extends BaseService
'opened_date' => $stockEntry->opened_date, 'opened_date' => $stockEntry->opened_date,
'location_id' => $stockEntry->location_id, 'location_id' => $stockEntry->location_id,
'correlation_id' => $correlationId, 'correlation_id' => $correlationId,
'transaction_Id' => $transactionId 'transaction_Id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRowForLocationFrom->save(); $logRowForLocationFrom->save();
@ -930,7 +938,8 @@ class StockService extends BaseService
'opened_date' => $stockEntry->opened_date, 'opened_date' => $stockEntry->opened_date,
'location_id' => $locationIdTo, 'location_id' => $locationIdTo,
'correlation_id' => $correlationId, 'correlation_id' => $correlationId,
'transaction_Id' => $transactionId 'transaction_Id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRowForLocationTo->save(); $logRowForLocationTo->save();
@ -957,7 +966,8 @@ class StockService extends BaseService
'opened_date' => $stockEntry->opened_date, 'opened_date' => $stockEntry->opened_date,
'location_id' => $stockEntry->location_id, 'location_id' => $stockEntry->location_id,
'correlation_id' => $correlationId, 'correlation_id' => $correlationId,
'transaction_Id' => $transactionId 'transaction_Id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRowForLocationFrom->save(); $logRowForLocationFrom->save();
@ -973,7 +983,8 @@ class StockService extends BaseService
'opened_date' => $stockEntry->opened_date, 'opened_date' => $stockEntry->opened_date,
'location_id' => $locationIdTo, 'location_id' => $locationIdTo,
'correlation_id' => $correlationId, 'correlation_id' => $correlationId,
'transaction_Id' => $transactionId 'transaction_Id' => $transactionId,
'user_id' => GROCY_USER_ID
]); ]);
$logRowForLocationTo->save(); $logRowForLocationTo->save();

View File

@ -5,12 +5,15 @@
@section('viewJsName', 'stockjournal') @section('viewJsName', 'stockjournal')
@section('content') @section('content')
<div class="row"> <div class="title-related-links">
<div class="col">
<h2 class="title">@yield('title')</h2> <h2 class="title">@yield('title')</h2>
<div class="related-links">
<a class="btn btn-outline-dark responsive-button"
href="{{ $U('/stockjournal/summary') }}">
{{ $__t('Journal summary') }}
</a>
</div> </div>
</div> </div>
<hr> <hr>
<div class="row my-3"> <div class="row my-3">
<div class="col-xs-12 col-md-6 col-xl-3"> <div class="col-xs-12 col-md-6 col-xl-3">
@ -52,6 +55,7 @@
<th>{{ $__t('Booking time') }}</th> <th>{{ $__t('Booking time') }}</th>
<th>{{ $__t('Booking type') }}</th> <th>{{ $__t('Booking type') }}</th>
<th class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">{{ $__t('Location') }}</th> <th class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">{{ $__t('Location') }}</th>
<th>{{ $__t('Done by') }}</th>
</tr> </tr>
</thead> </thead>
<tbody class="d-none"> <tbody class="d-none">
@ -70,7 +74,7 @@
</a> </a>
</td> </td>
<td> <td>
<span class="name-anchor @if($stockLogEntry->undone == 1) text-strike-through @endif">{{ FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->name }}</span> <span class="name-anchor @if($stockLogEntry->undone == 1) text-strike-through @endif">{{ $stockLogEntry->product_name }}</span>
@if($stockLogEntry->undone == 1) @if($stockLogEntry->undone == 1)
<br> <br>
{{ $__t('Undone on') . ' ' . $stockLogEntry->undone_timestamp }} {{ $__t('Undone on') . ' ' . $stockLogEntry->undone_timestamp }}
@ -79,7 +83,7 @@
@endif @endif
</td> </td>
<td> <td>
<span class="locale-number locale-number-quantity-amount">{{ $stockLogEntry->amount }}</span> {{ $__n($stockLogEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockLogEntry->product_id)->qu_id_stock)->name_plural) }} <span class="locale-number locale-number-quantity-amount">{{ $stockLogEntry->amount }}</span> {{ $__n($stockLogEntry->amount, $stockLogEntry->qu_name, $stockLogEntry->qu_name_plural) }}
</td> </td>
<td> <td>
{{ $stockLogEntry->row_created_timestamp }} {{ $stockLogEntry->row_created_timestamp }}
@ -93,7 +97,10 @@
@endif @endif
</td> </td>
<td class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif"> <td class="@if(!GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING) d-none @endif">
{{ FindObjectInArrayByPropertyValue($locations, 'id', $stockLogEntry->location_id)->name }} {{ $stockLogEntry->location_name }}
</td>
<td>
{{ $stockLogEntry->user_display_name }}
</td> </td>
</tr> </tr>
@endforeach @endforeach

View File

@ -0,0 +1,47 @@
@extends('layout.default')
@section('title', $__t('Stock journal summary'))
@section('activeNav', '')
@section('viewJsName', 'stockjournalsummary')
@section('content')
<div class="row">
<div class="col">
<h2 class="title">@yield('title')</h2>
</div>
</div>
<hr>
<div class="row">
<div class="col">
<table id="journal-summary-table"
class="table table-sm table-striped dt-responsive">
<thead>
<tr>
<th>{{ $__t('Product') }}</th>
<th>{{ $__t('Booking type') }}</th>
<th>{{ $__t('User') }}</th>
<th>{{ $__t('Amount') }}</th>
</tr>
</thead>
<tbody class="d-none">
@foreach($entries as $journalEntry)
<tr>
<td>
{{ $journalEntry->product_name }}
</td>
<td>
{{ $__t($journalEntry->transaction_type) }}
</td>
<td>
{{ $journalEntry->user_display_name }}
</td>
<td>
<span class="locale-number locale-number-quantity-amount">{{ $journalEntry->amount }}</span> {{ $__n($journalEntry->amount, $journalEntry->qu_name, $journalEntry->qu_name_plural) }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@stop

View File

@ -142,7 +142,9 @@
<tbody class="d-none"> <tbody class="d-none">
@foreach($currentStock as $currentStockEntry) @foreach($currentStock as $currentStockEntry)
<tr id="product-{{ $currentStockEntry->product_id }}-row" <tr id="product-{{ $currentStockEntry->product_id }}-row"
class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) table-danger @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("+$nextXDays days")) class="@if(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime('-1 days')) && $currentStockEntry->amount > 0) table-danger @elseif(GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING && $currentStockEntry->best_before_date < date('Y-m-d 23:59:59', strtotime("
+$nextXDays
days"))
&& &&
$currentStockEntry->amount > 0) table-warning @elseif ($currentStockEntry->product_missing) table-info @endif"> $currentStockEntry->amount > 0) table-warning @elseif ($currentStockEntry->product_missing) table-info @endif">
<td class="fit-content border-right"> <td class="fit-content border-right">
@ -246,6 +248,11 @@
href="{{ $U('/stockjournal?product=') }}{{ $currentStockEntry->product_id }}"> href="{{ $U('/stockjournal?product=') }}{{ $currentStockEntry->product_id }}">
<span class="dropdown-item-icon"><i class="fas fa-file-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Stock journal for this product') }}</span> <span class="dropdown-item-icon"><i class="fas fa-file-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Stock journal for this product') }}</span>
</a> </a>
<a class="dropdown-item"
type="button"
href="{{ $U('/stockjournal/summary?product_id=') }}{{ $currentStockEntry->product_id }}">
<span class="dropdown-item-icon"><i class="fas fa-file-archive"></i></span> <span class="dropdown-item-text">{{ $__t('Journal summary for this product') }}</span>
</a>
<a class="dropdown-item permission-MASTER_DATA_EDIT" <a class="dropdown-item permission-MASTER_DATA_EDIT"
type="button" type="button"
href="{{ $U('/product/') }}{{ $currentStockEntry->product_id . '?returnto=%2Fstockoverview' }}"> href="{{ $U('/product/') }}{{ $currentStockEntry->product_id . '?returnto=%2Fstockoverview' }}">
@ -322,7 +329,8 @@
&& &&
$currentStockEntry->amount > 0) expired @elseif($currentStockEntry->best_before_date < date('Y-m-d $currentStockEntry->amount > 0) expired @elseif($currentStockEntry->best_before_date < date('Y-m-d
23:59:59', 23:59:59',
strtotime("+$nextXDays days")) strtotime("+$nextXDays
days"))
&& &&
$currentStockEntry->amount > 0) expiring @endif @if($currentStockEntry->product_missing) belowminstockamount @endif $currentStockEntry->amount > 0) expiring @endif @if($currentStockEntry->product_missing) belowminstockamount @endif
</td> </td>