mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 01:32:38 +00:00
Added the possibility to merge chores
This commit is contained in:
parent
693dcc1020
commit
4d21668265
@ -3,6 +3,7 @@
|
||||
- Added the products average price as a (hidden by default) column on the stock overview page
|
||||
- Added a new "Presets for new products" stock setting for the "Default due days" option of new products
|
||||
- When adding (purchase) a product with "Default due days after freezing" set directly to a freezer location, the due date is now prefilled by that (instead of the normal "Default due days") (thanks @grahamc for the initial work on this)
|
||||
- Chores can now be merged (new dropdown menu item on the chores list page)
|
||||
- Fixed that the labels of context-/more-menu items were not readable in Night Mode (thanks @corbolais)
|
||||
- Fixed that "Label per unit" stock entry labels (on purchase) weren't unique per unit
|
||||
- Fixed that the "Add as new product" productpicker workflow, started from the shopping list item form, always selected the default shopping list after finishing the flow
|
||||
@ -13,5 +14,6 @@
|
||||
- Fixed that the "Stay logged in permanently" checkbox on the login page had no effect (thanks @0)
|
||||
|
||||
### API
|
||||
- New endpoint `/chores/{choreIdToKeep}/merge/{choreIdToRemove}` for merging chores
|
||||
- Endpoint `/stock/products/{productId}/add` API endpoint`: The (optional) request body parameter `print_stock_label` was renamed to `stock_label_type`
|
||||
- Fixed that backslashes were not allowed in API query filters
|
||||
|
@ -131,4 +131,24 @@ class ChoresApiController extends BaseApiController
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function MergeChores(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
||||
{
|
||||
User::checkPermission($request, User::PERMISSION_MASTER_DATA_EDIT);
|
||||
|
||||
try
|
||||
{
|
||||
if (filter_var($args['choreIdToKeep'], FILTER_VALIDATE_INT) === false || filter_var($args['choreIdToRemove'], FILTER_VALIDATE_INT) === false)
|
||||
{
|
||||
throw new \Exception('Provided {choreIdToKeep} or {choreIdToRemove} is not a valid integer');
|
||||
}
|
||||
|
||||
$this->ApiResponse($response, $this->getChoresService()->MergeChores($args['choreIdToKeep'], $args['choreIdToRemove']));
|
||||
return $this->EmptyApiResponse($response);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3779,6 +3779,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/chores/{choreIdToKeep}/merge/{choreIdToRemove}": {
|
||||
"post": {
|
||||
"summary": "Merges two chores into one",
|
||||
"tags": [
|
||||
"Chores"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "choreIdToKeep",
|
||||
"required": true,
|
||||
"description": "A valid chore id of the chore to keep",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "choreIdToRemove",
|
||||
"required": true,
|
||||
"description": "A valid chore id of the chore to remove",
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "The operation was successful"
|
||||
},
|
||||
"400": {
|
||||
"description": "The operation was not successful (possible errors are: Invalid chore id)",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error400"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/batteries": {
|
||||
"get": {
|
||||
"summary": "Returns all batteries incl. the next estimated charge time per battery",
|
||||
|
@ -2048,7 +2048,7 @@ msgstr ""
|
||||
msgid "Product to remove"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while merging products"
|
||||
msgid "Error while merging"
|
||||
msgstr ""
|
||||
|
||||
msgid "After merging, this product will be kept"
|
||||
@ -2244,3 +2244,18 @@ msgstr ""
|
||||
|
||||
msgid "This is the default which will be prefilled on purchase"
|
||||
msgstr ""
|
||||
|
||||
msgid "Merge chores"
|
||||
msgstr ""
|
||||
|
||||
msgid "Chore to keep"
|
||||
msgstr ""
|
||||
|
||||
msgid "After merging, this chore will be kept"
|
||||
msgstr ""
|
||||
|
||||
msgid "Chore to remove"
|
||||
msgstr ""
|
||||
|
||||
msgid "After merging, all occurences of this chore will be replaced by the kept chore (means this chore will not exist anymore)"
|
||||
msgstr ""
|
||||
|
@ -79,3 +79,28 @@ if (GetUriParam('include_disabled'))
|
||||
{
|
||||
$("#show-disabled").prop('checked', true);
|
||||
}
|
||||
|
||||
$(".merge-chores-button").on("click", function(e)
|
||||
{
|
||||
var choreId = $(e.currentTarget).attr("data-chore-id");
|
||||
$("#merge-chores-keep").val(choreId);
|
||||
$("#merge-chores-remove").val("");
|
||||
$("#merge-chores-modal").modal("show");
|
||||
});
|
||||
|
||||
$("#merge-chores-save-button").on("click", function()
|
||||
{
|
||||
var choreIdToKeep = $("#merge-chores-keep").val();
|
||||
var choreIdToRemove = $("#merge-chores-remove").val();
|
||||
|
||||
Grocy.Api.Post("chores/" + choreIdToKeep.toString() + "/merge/" + choreIdToRemove.toString(), {},
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/chores');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while merging', xhr.response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -147,7 +147,7 @@ $("#merge-products-save-button").on("click", function()
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while merging products', xhr.response);
|
||||
Grocy.FrontendHelpers.ShowGenericError('Error while merging', xhr.response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -238,6 +238,7 @@ $app->group('/api', function (RouteCollectorProxy $group) {
|
||||
$group->post('/chores/executions/{executionId}/undo', '\Grocy\Controllers\ChoresApiController:UndoChoreExecution');
|
||||
$group->post('/chores/executions/calculate-next-assignments', '\Grocy\Controllers\ChoresApiController:CalculateNextExecutionAssignments');
|
||||
$group->get('/chores/{choreId}/printlabel', '\Grocy\Controllers\ChoresApiController:ChorePrintLabel');
|
||||
$group->post('/chores/{choreIdToKeep}/merge/{choreIdToRemove}', '\Grocy\Controllers\ChoresApiController:MergeChores');
|
||||
|
||||
//Printing
|
||||
$group->get('/print/shoppinglist/thermal', '\Grocy\Controllers\PrintApiController:PrintShoppingListThermal');
|
||||
|
@ -209,6 +209,40 @@ class ChoresService extends BaseService
|
||||
]);
|
||||
}
|
||||
|
||||
public function MergeChores(int $choreIdToKeep, int $choreIdToRemove)
|
||||
{
|
||||
if (!$this->ChoreExists($choreIdToKeep))
|
||||
{
|
||||
throw new \Exception('$choreIdToKeep does not exist or is inactive');
|
||||
}
|
||||
|
||||
if (!$this->ChoreExists($choreIdToRemove))
|
||||
{
|
||||
throw new \Exception('$choreIdToRemove does not exist or is inactive');
|
||||
}
|
||||
|
||||
if ($choreIdToKeep == $choreIdToRemove)
|
||||
{
|
||||
throw new \Exception('$choreIdToKeep cannot equal $choreIdToRemove');
|
||||
}
|
||||
|
||||
$this->getDatabaseService()->GetDbConnectionRaw()->beginTransaction();
|
||||
try
|
||||
{
|
||||
$choreToKeep = $this->getDatabase()->chores($choreIdToKeep);
|
||||
$choreToRemove = $this->getDatabase()->chores($choreIdToRemove);
|
||||
|
||||
$this->getDatabaseService()->ExecuteDbStatement('UPDATE chores_log SET chore_id = ' . $choreIdToKeep . ' WHERE chore_id = ' . $choreIdToRemove);
|
||||
$this->getDatabaseService()->ExecuteDbStatement('DELETE FROM chores WHERE id = ' . $choreIdToRemove);
|
||||
}
|
||||
catch (Exception $ex)
|
||||
{
|
||||
$this->getDatabaseService()->GetDbConnectionRaw()->rollback();
|
||||
throw $ex;
|
||||
}
|
||||
$this->getDatabaseService()->GetDbConnectionRaw()->commit();
|
||||
}
|
||||
|
||||
private function ChoreExists($choreId)
|
||||
{
|
||||
$choreRow = $this->getDatabase()->chores()->where('id = :1', $choreId)->fetch();
|
||||
|
@ -116,6 +116,21 @@
|
||||
title="{{ $__t('Delete this item') }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item merge-chores-button"
|
||||
data-chore-id="{{ $chore->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Merge') }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ $chore->name }}
|
||||
@ -138,4 +153,55 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade"
|
||||
id="merge-chores-modal"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content text-center">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title w-100">{{ $__t('Merge chores') }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="merge-chores-keep">{{ $__t('Chore to keep') }} <i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('After merging, this chore will be kept') }}"></i>
|
||||
</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="merge-chores-keep">
|
||||
<option></option>
|
||||
@foreach($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="merge-chores-remove">{{ $__t('Chore to remove') }} <i class="fas fa-question-circle text-muted"
|
||||
data-toggle="tooltip"
|
||||
data-trigger="hover click"
|
||||
title="{{ $__t('After merging, all occurences of this chore will be replaced by the kept chore (means this chore will not exist anymore)') }}"></i>
|
||||
</label>
|
||||
<select class="custom-control custom-select"
|
||||
id="merge-chores-remove">
|
||||
<option></option>
|
||||
@foreach($chores as $chore)
|
||||
<option value="{{ $chore->id }}">{{ $chore->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||
<button id="merge-chores-save-button"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
data-dismiss="modal">{{ $__t('OK') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
|
Loading…
x
Reference in New Issue
Block a user