Support associating a Shopping List with a Store

This commit is contained in:
Riccardo Sacchetto 2025-07-13 01:31:33 +02:00
parent 6b18b0a7be
commit 4fb70f8a89
No known key found for this signature in database
GPG Key ID: AE51E67314D79BA2
8 changed files with 71 additions and 23 deletions

View File

@ -20,13 +20,18 @@ class StockApiController extends BaseApiController
$requestBody = $this->GetParsedAndFilteredRequestBody($request);
$listId = 1;
if (array_key_exists('list_id', $requestBody) && !empty($requestBody['list_id']) && is_numeric($requestBody['list_id']))
{
$listId = intval($requestBody['list_id']);
}
$this->getStockService()->AddMissingProductsToShoppingList($listId);
$checkDefaultShoppingLocation = false;
if (array_key_exists('check_default_shopping_location', $requestBody) && filter_var($requestBody['check_default_shopping_location'], FILTER_VALIDATE_BOOLEAN) !== false)
{
$checkDefaultShoppingLocation = boolval($requestBody['check_default_shopping_location']);
}
$this->getStockService()->AddMissingProductsToShoppingList($listId, $checkDefaultShoppingLocation);
return $this->EmptyApiResponse($response);
}
catch (\Exception $ex)

View File

@ -441,6 +441,7 @@ class StockController extends BaseController
{
return $this->renderPage($response, 'shoppinglistform', [
'mode' => 'create',
'shoppinglocations' => $this->getDatabase()->shopping_locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
'userfields' => $this->getUserfieldsService()->GetFields('shopping_lists')
]);
}
@ -449,6 +450,7 @@ class StockController extends BaseController
return $this->renderPage($response, 'shoppinglistform', [
'shoppingList' => $this->getDatabase()->shopping_lists($args['listId']),
'mode' => 'edit',
'shoppinglocations' => $this->getDatabase()->shopping_locations()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'),
'userfields' => $this->getUserfieldsService()->GetFields('shopping_lists')
]);
}

View File

@ -2470,3 +2470,6 @@ msgstr ""
msgid "List actions"
msgstr ""
msgid "Add products that are below defined min. stock amount from matching store"
msgstr ""

2
migrations/0254.sql Normal file
View File

@ -0,0 +1,2 @@
ALTER TABLE shopping_lists
ADD shopping_location_id REFERENCES shopping_locations(id);

View File

@ -156,6 +156,20 @@ $(document).on('click', '#add-products-below-min-stock-amount', function(e)
);
});
$(document).on('click', '#add-products-below-min-stock-amount-by-shopping-location', function(e)
{
Grocy.Api.Post('stock/shoppinglist/add-missing-products', { "list_id": $("#selected-shopping-list").val(), "check_default_shopping_location": true },
function(result)
{
window.location.href = U('/shoppinglist?list=' + $("#selected-shopping-list").val());
},
function(xhr)
{
console.error(xhr);
}
);
});
$(document).on('click', '#add-overdue-expired-products', function(e)
{
Grocy.Api.Post('stock/shoppinglist/add-overdue-products', { "list_id": $("#selected-shopping-list").val() },

View File

@ -18,41 +18,46 @@ class StockService extends BaseService
const TRANSACTION_TYPE_TRANSFER_FROM = 'transfer_from';
const TRANSACTION_TYPE_TRANSFER_TO = 'transfer_to';
public function AddMissingProductsToShoppingList($listId = 1)
public function AddMissingProductsToShoppingList($listId = 1, $checkDefaultShoppingLocation = False)
{
if (!$this->ShoppingListExists($listId))
{
throw new \Exception('Shopping list does not exist');
}
$shoppingList = $this->getDatabase()->shopping_lists()->where('id', $listId)->fetch();
$missingProducts = $this->GetMissingProducts();
foreach ($missingProducts as $missingProduct)
{
$product = $this->getDatabase()->products()->where('id', $missingProduct->id)->fetch();
$amountToAdd = round($missingProduct->amount_missing, 2);
$alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id', $missingProduct->id)->fetch();
if ($alreadyExistingEntry)
if (!$checkDefaultShoppingLocation || ($product->shopping_location_id == $shoppingList->shopping_location_id))
{
// Update
if ($alreadyExistingEntry->amount < $amountToAdd)
$amountToAdd = round($missingProduct->amount_missing, 2);
$alreadyExistingEntry = $this->getDatabase()->shopping_list()->where('product_id', $missingProduct->id)->fetch();
if ($alreadyExistingEntry)
{
$alreadyExistingEntry->update([
'amount' => $amountToAdd,
'shopping_list_id' => $listId
]);
// Update
if ($alreadyExistingEntry->amount < $amountToAdd)
{
$alreadyExistingEntry->update([
'amount' => $amountToAdd,
'shopping_list_id' => $listId
]);
}
}
else
{
// Insert
$shoppinglistRow = $this->getDatabase()->shopping_list()->createRow([
'product_id' => $missingProduct->id,
'amount' => $amountToAdd,
'shopping_list_id' => $listId,
'qu_id' => $product->qu_id_purchase
]);
$shoppinglistRow->save();
}
}
else
{
// Insert
$shoppinglistRow = $this->getDatabase()->shopping_list()->createRow([
'product_id' => $missingProduct->id,
'amount' => $amountToAdd,
'shopping_list_id' => $listId,
'qu_id' => $product->qu_id_purchase
]);
$shoppinglistRow->save();
}
}
}

View File

@ -144,6 +144,9 @@ $listItem->last_price_total = $listItem->price * $listItem->amount;
<a id="add-products-below-min-stock-amount"
class="dropdown-item"
href="#">{{ $__t('Add products that are below defined min. stock amount') }}</a>
<a id="add-products-below-min-stock-amount-by-shopping-location"
class="dropdown-item"
href="#">{{ $__t('Add products that are below defined min. stock amount from matching store') }}</a>
@endif
<a id="add-overdue-expired-products"
class="dropdown-item"

View File

@ -41,6 +41,20 @@
<div class="invalid-feedback">{{ $__t('A name is required') }}</div>
</div>
@php $prefillById = ''; if($mode=='edit') { $prefillById = $shoppingList->shopping_location_id; } @endphp
@if(GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
@include('components.shoppinglocationpicker', array(
'label' => 'Associated store',
'prefillById' => $prefillById,
'shoppinglocations' => $shoppinglocations
))
@else
<input type="hidden"
name="shopping_location_id"
id="shopping_location_id"
value="1">
@endif
@include('components.userfieldsform', array(
'userfields' => $userfields,
'entity' => 'shopping_lists'