make food onhand when complete shopping entry
This commit is contained in:
parent
7c89117e04
commit
2c76fb7b69
@ -481,7 +481,7 @@ class ShoppingPreferenceForm(forms.ModelForm):
|
|||||||
|
|
||||||
fields = (
|
fields = (
|
||||||
'shopping_share', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'mealplan_autoexclude_onhand',
|
'shopping_share', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'mealplan_autoexclude_onhand',
|
||||||
'mealplan_autoinclude_related', 'default_delay', 'filter_to_supermarket', 'shopping_recent_days', 'csv_delim', 'csv_prefix'
|
'mealplan_autoinclude_related', 'shopping_add_onhand', 'default_delay', 'filter_to_supermarket', 'shopping_recent_days', 'csv_delim', 'csv_prefix'
|
||||||
)
|
)
|
||||||
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
@ -496,6 +496,7 @@ class ShoppingPreferenceForm(forms.ModelForm):
|
|||||||
'default_delay': _('Default number of hours to delay a shopping list entry.'),
|
'default_delay': _('Default number of hours to delay a shopping list entry.'),
|
||||||
'filter_to_supermarket': _('Filter shopping list to only include supermarket categories.'),
|
'filter_to_supermarket': _('Filter shopping list to only include supermarket categories.'),
|
||||||
'shopping_recent_days': _('Days of recent shopping list entries to display.'),
|
'shopping_recent_days': _('Days of recent shopping list entries to display.'),
|
||||||
|
'shopping_add_onhand': _("Mark food 'On Hand' when checked off shopping list."),
|
||||||
'csv_delim': _('Delimiter to use for CSV exports.'),
|
'csv_delim': _('Delimiter to use for CSV exports.'),
|
||||||
'csv_prefix': _('Prefix to add when copying list to the clipboard.'),
|
'csv_prefix': _('Prefix to add when copying list to the clipboard.'),
|
||||||
|
|
||||||
@ -510,7 +511,8 @@ class ShoppingPreferenceForm(forms.ModelForm):
|
|||||||
'filter_to_supermarket': _('Filter to Supermarket'),
|
'filter_to_supermarket': _('Filter to Supermarket'),
|
||||||
'shopping_recent_days': _('Recent Days'),
|
'shopping_recent_days': _('Recent Days'),
|
||||||
'csv_delim': _('CSV Delimiter'),
|
'csv_delim': _('CSV Delimiter'),
|
||||||
"csv_prefix_label": _("List Prefix")
|
"csv_prefix_label": _("List Prefix"),
|
||||||
|
'shopping_add_onhand': _("Auto On Hand"),
|
||||||
}
|
}
|
||||||
|
|
||||||
widgets = {
|
widgets = {
|
||||||
|
@ -81,7 +81,7 @@ def list_from_recipe(list_recipe=None, recipe=None, mealplan=None, servings=None
|
|||||||
ingredients = Ingredient.objects.filter(step__recipe=r, space=space)
|
ingredients = Ingredient.objects.filter(step__recipe=r, space=space)
|
||||||
|
|
||||||
if exclude_onhand := created_by.userpreference.mealplan_autoexclude_onhand:
|
if exclude_onhand := created_by.userpreference.mealplan_autoexclude_onhand:
|
||||||
ingredients = ingredients.exclude(food__food_onhand=True)
|
ingredients = ingredients.exclude(food__onhand_users__id__in=[x.id for x in shared_users])
|
||||||
|
|
||||||
if related := created_by.userpreference.mealplan_autoinclude_related:
|
if related := created_by.userpreference.mealplan_autoinclude_related:
|
||||||
# TODO: add levels of related recipes (related recipes of related recipes) to use when auto-adding mealplans
|
# TODO: add levels of related recipes (related recipes of related recipes) to use when auto-adding mealplans
|
||||||
@ -92,7 +92,7 @@ def list_from_recipe(list_recipe=None, recipe=None, mealplan=None, servings=None
|
|||||||
# TODO once/if Steps can have a serving size this needs to be refactored
|
# TODO once/if Steps can have a serving size this needs to be refactored
|
||||||
if exclude_onhand:
|
if exclude_onhand:
|
||||||
# if steps are used more than once in a recipe or subrecipe - I don' think this results in the desired behavior
|
# if steps are used more than once in a recipe or subrecipe - I don' think this results in the desired behavior
|
||||||
related_step_ing += Ingredient.objects.filter(step__recipe=x, food__food_onhand=False, space=space).values_list('id', flat=True)
|
related_step_ing += Ingredient.objects.filter(step__recipe=x, space=space).exclude(food__onhand_users__id__in=[x.id for x in shared_users]).values_list('id', flat=True)
|
||||||
else:
|
else:
|
||||||
related_step_ing += Ingredient.objects.filter(step__recipe=x, space=space).values_list('id', flat=True)
|
related_step_ing += Ingredient.objects.filter(step__recipe=x, space=space).values_list('id', flat=True)
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ def list_from_recipe(list_recipe=None, recipe=None, mealplan=None, servings=None
|
|||||||
if ingredients.filter(food__recipe=x).exists():
|
if ingredients.filter(food__recipe=x).exists():
|
||||||
for ing in ingredients.filter(food__recipe=x):
|
for ing in ingredients.filter(food__recipe=x):
|
||||||
if exclude_onhand:
|
if exclude_onhand:
|
||||||
x_ing = Ingredient.objects.filter(step__recipe=x, food__food_onhand=False, space=space)
|
x_ing = Ingredient.objects.filter(step__recipe=x, space=space).exclude(food__onhand_users__id__in=[x.id for x in shared_users])
|
||||||
else:
|
else:
|
||||||
x_ing = Ingredient.objects.filter(step__recipe=x, space=space)
|
x_ing = Ingredient.objects.filter(step__recipe=x, space=space)
|
||||||
for i in [x for x in x_ing]:
|
for i in [x for x in x_ing]:
|
||||||
|
@ -526,10 +526,10 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
|
|||||||
])
|
])
|
||||||
|
|
||||||
inherit = inherit.values_list('field', flat=True)
|
inherit = inherit.values_list('field', flat=True)
|
||||||
if 'food_onhand' in inherit:
|
if 'ignore_shopping' in inherit:
|
||||||
# get food at root that have children that need updated
|
# get food at root that have children that need updated
|
||||||
Food.include_descendants(queryset=Food.objects.filter(depth=1, numchild__gt=0, space=space, food_onhand=True)).update(food_onhand=True)
|
Food.include_descendants(queryset=Food.objects.filter(depth=1, numchild__gt=0, space=space, ignore_shopping=True)).update(ignore_shopping=True)
|
||||||
Food.include_descendants(queryset=Food.objects.filter(depth=1, numchild__gt=0, space=space, food_onhand=False)).update(food_onhand=False)
|
Food.include_descendants(queryset=Food.objects.filter(depth=1, numchild__gt=0, space=space, ignore_shopping=False)).update(ignore_shopping=False)
|
||||||
if 'supermarket_category' in inherit:
|
if 'supermarket_category' in inherit:
|
||||||
# when supermarket_category is null or blank assuming it is not set and not intended to be blank for all descedants
|
# when supermarket_category is null or blank assuming it is not set and not intended to be blank for all descedants
|
||||||
# find top node that has category set
|
# find top node that has category set
|
||||||
|
@ -10,6 +10,7 @@ from django.utils import timezone
|
|||||||
from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer
|
from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import NotFound, ValidationError
|
from rest_framework.exceptions import NotFound, ValidationError
|
||||||
|
from rest_framework.fields import SkipField
|
||||||
|
|
||||||
from cookbook.helper.shopping_helper import list_from_recipe
|
from cookbook.helper.shopping_helper import list_from_recipe
|
||||||
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food,
|
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food,
|
||||||
@ -176,16 +177,13 @@ class UserPreferenceSerializer(serializers.ModelSerializer):
|
|||||||
raise NotFound()
|
raise NotFound()
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
|
||||||
# don't allow writing to FoodInheritField via API
|
|
||||||
return super().update(instance, validated_data)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserPreference
|
model = UserPreference
|
||||||
fields = (
|
fields = (
|
||||||
'user', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_kj', 'search_style', 'show_recent', 'plan_share',
|
'user', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_kj', 'search_style', 'show_recent', 'plan_share',
|
||||||
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'food_inherit_default', 'default_delay',
|
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'food_inherit_default', 'default_delay',
|
||||||
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days', 'csv_delim', 'csv_prefix', 'filter_to_supermarket'
|
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days', 'csv_delim', 'csv_prefix',
|
||||||
|
'filter_to_supermarket', 'shopping_add_onhand'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -380,23 +378,23 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
|||||||
recipe = RecipeSimpleSerializer(allow_null=True, required=False)
|
recipe = RecipeSimpleSerializer(allow_null=True, required=False)
|
||||||
shopping = serializers.SerializerMethodField('get_shopping_status')
|
shopping = serializers.SerializerMethodField('get_shopping_status')
|
||||||
inherit_fields = FoodInheritFieldSerializer(many=True, allow_null=True, required=False)
|
inherit_fields = FoodInheritFieldSerializer(many=True, allow_null=True, required=False)
|
||||||
food_onhand = CustomOnHandField()
|
food_onhand = CustomOnHandField(required=False)
|
||||||
|
|
||||||
recipe_filter = 'steps__ingredients__food'
|
recipe_filter = 'steps__ingredients__food'
|
||||||
|
|
||||||
def get_shopping_status(self, obj):
|
def get_shopping_status(self, obj):
|
||||||
return ShoppingListEntry.objects.filter(space=obj.space, food=obj, checked=False).count() > 0
|
return ShoppingListEntry.objects.filter(space=obj.space, food=obj, checked=False).count() > 0
|
||||||
|
|
||||||
# def get_food_onhand(self, obj):
|
|
||||||
# shared_users = [x.id for x in list(self.context['request'].user.get_shopping_share())] + [self.context['request'].user.id]
|
|
||||||
# return obj.onhand_users.filter(id__in=shared_users).exists()
|
|
||||||
|
|
||||||
def run_validation(self, data):
|
def run_validation(self, data):
|
||||||
validated_data = super().run_validation(data)
|
try:
|
||||||
|
validated_data = super().run_validation(data)
|
||||||
|
except SkipField:
|
||||||
|
return super().run_validation(data)
|
||||||
# convert boolean food_onhand to onhand_users
|
# convert boolean food_onhand to onhand_users
|
||||||
|
# TODO maybe this should be moved to pre_save signal to ensure shopping_share users are always included?
|
||||||
if (
|
if (
|
||||||
self.root.instance.__class__.__name__ == 'Food'
|
self.root.instance.__class__.__name__ == 'Food'
|
||||||
and not (onhand := data.pop('food_onhand', None)) is None
|
and not onhand is None
|
||||||
):
|
):
|
||||||
# assuming if on hand for user also onhand for shopping_share users
|
# assuming if on hand for user also onhand for shopping_share users
|
||||||
shared_users = [user := self.context['request'].user] + list(user.userpreference.shopping_share.all())
|
shared_users = [user := self.context['request'].user] + list(user.userpreference.shopping_share.all())
|
||||||
@ -404,6 +402,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
|||||||
validated_data['onhand_users'] = list(self.instance.onhand_users.all()) + shared_users
|
validated_data['onhand_users'] = list(self.instance.onhand_users.all()) + shared_users
|
||||||
else:
|
else:
|
||||||
validated_data['onhand_users'] = list(set(self.instance.onhand_users.all()) - set(shared_users))
|
validated_data['onhand_users'] = list(set(self.instance.onhand_users.all()) - set(shared_users))
|
||||||
|
|
||||||
return validated_data
|
return validated_data
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@ -724,6 +723,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
|||||||
):
|
):
|
||||||
# if checked flips from false to true set completed datetime
|
# if checked flips from false to true set completed datetime
|
||||||
data['completed_at'] = timezone.now()
|
data['completed_at'] = timezone.now()
|
||||||
|
|
||||||
elif not data.get('checked', False):
|
elif not data.get('checked', False):
|
||||||
# if not checked set completed to None
|
# if not checked set completed to None
|
||||||
data['completed_at'] = None
|
data['completed_at'] = None
|
||||||
@ -739,6 +739,16 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
|||||||
validated_data['created_by'] = self.context['request'].user
|
validated_data['created_by'] = self.context['request'].user
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
user = self.context['request'].user
|
||||||
|
if user.userpreference.shopping_add_onhand:
|
||||||
|
if checked := validated_data.get('checked', None):
|
||||||
|
instance.food.onhand_users.add(*user.userpreference.shopping_share.all(), user)
|
||||||
|
elif checked == False:
|
||||||
|
instance.food.onhand_users.remove(*user.userpreference.shopping_share.all(), user)
|
||||||
|
|
||||||
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShoppingListEntry
|
model = ShoppingListEntry
|
||||||
fields = (
|
fields = (
|
||||||
@ -891,7 +901,7 @@ class FoodExportSerializer(FoodSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Food
|
model = Food
|
||||||
fields = ('name', 'food_onhand', 'supermarket_category',)
|
fields = ('name', 'ignore_shopping', 'supermarket_category',)
|
||||||
|
|
||||||
|
|
||||||
class IngredientExportSerializer(WritableNestedModelSerializer):
|
class IngredientExportSerializer(WritableNestedModelSerializer):
|
||||||
|
@ -75,8 +75,8 @@ def update_food_inheritance(sender, instance=None, created=False, **kwargs):
|
|||||||
# apply changes from parent to instance for each inheritted field
|
# apply changes from parent to instance for each inheritted field
|
||||||
if instance.parent and inherit.count() > 0:
|
if instance.parent and inherit.count() > 0:
|
||||||
parent = instance.get_parent()
|
parent = instance.get_parent()
|
||||||
if 'food_onhand' in inherit:
|
if 'ignore_shopping' in inherit:
|
||||||
instance.food_onhand = parent.food_onhand
|
instance.ignore_shopping = parent.ignore_shopping
|
||||||
# if supermarket_category is not set, do not cascade - if this becomes non-intuitive can change
|
# if supermarket_category is not set, do not cascade - if this becomes non-intuitive can change
|
||||||
if 'supermarket_category' in inherit and parent.supermarket_category:
|
if 'supermarket_category' in inherit and parent.supermarket_category:
|
||||||
instance.supermarket_category = parent.supermarket_category
|
instance.supermarket_category = parent.supermarket_category
|
||||||
@ -89,8 +89,8 @@ def update_food_inheritance(sender, instance=None, created=False, **kwargs):
|
|||||||
# TODO figure out how to generalize this
|
# TODO figure out how to generalize this
|
||||||
# apply changes to direct children - depend on save signals for those objects to cascade inheritance down
|
# apply changes to direct children - depend on save signals for those objects to cascade inheritance down
|
||||||
_save = []
|
_save = []
|
||||||
for child in instance.get_children().filter(inherit_fields__field='food_onhand'):
|
for child in instance.get_children().filter(inherit_fields__field='ignore_shopping'):
|
||||||
child.food_onhand = instance.food_onhand
|
child.ignore_shopping = instance.ignore_shopping
|
||||||
_save.append(child)
|
_save.append(child)
|
||||||
# don't cascade empty supermarket category
|
# don't cascade empty supermarket category
|
||||||
if instance.supermarket_category:
|
if instance.supermarket_category:
|
||||||
@ -121,3 +121,11 @@ def auto_add_shopping(sender, instance=None, created=False, weak=False, **kwargs
|
|||||||
'servings': instance.servings
|
'servings': instance.servings
|
||||||
}
|
}
|
||||||
list_recipe = list_from_recipe(**kwargs)
|
list_recipe = list_from_recipe(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# user = self.context['request'].user
|
||||||
|
# if user.userpreference.shopping_add_onhand:
|
||||||
|
# if checked := validated_data.get('checked', None):
|
||||||
|
# instance.food.onhand_users.add(*user.userpreference.shopping_share.all(), user)
|
||||||
|
# elif checked == False:
|
||||||
|
# instance.food.onhand_users.remove(*user.userpreference.shopping_share.all(), user)
|
||||||
|
@ -483,8 +483,8 @@ def test_tree_filter(obj_tree_1, obj_2, obj_3, u1_s1):
|
|||||||
@pytest.mark.parametrize("obj_tree_1, field, inherit, new_val", [
|
@pytest.mark.parametrize("obj_tree_1, field, inherit, new_val", [
|
||||||
({'has_category': True, 'inherit': True}, 'supermarket_category', True, 'cat_1'),
|
({'has_category': True, 'inherit': True}, 'supermarket_category', True, 'cat_1'),
|
||||||
({'has_category': True, 'inherit': False}, 'supermarket_category', False, 'cat_1'),
|
({'has_category': True, 'inherit': False}, 'supermarket_category', False, 'cat_1'),
|
||||||
({'food_onhand': True, 'inherit': True}, 'food_onhand', True, 'false'),
|
({'ignore_shopping': True, 'inherit': True}, 'ignore_shopping', True, 'false'),
|
||||||
({'food_onhand': True, 'inherit': False}, 'food_onhand', False, 'false'),
|
({'ignore_shopping': True, 'inherit': False}, 'ignore_shopping', False, 'false'),
|
||||||
], indirect=['obj_tree_1']) # indirect=True populates magic variable request.param of obj_tree_1 with the parameter
|
], indirect=['obj_tree_1']) # indirect=True populates magic variable request.param of obj_tree_1 with the parameter
|
||||||
def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
|
def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
|
||||||
with scope(space=obj_tree_1.space):
|
with scope(space=obj_tree_1.space):
|
||||||
@ -509,16 +509,16 @@ def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("obj_tree_1", [
|
@pytest.mark.parametrize("obj_tree_1", [
|
||||||
({'has_category': True, 'inherit': False, 'food_onhand': True}),
|
({'has_category': True, 'inherit': False, 'ignore_shopping': True}),
|
||||||
], indirect=['obj_tree_1'])
|
], indirect=['obj_tree_1'])
|
||||||
def test_reset_inherit(obj_tree_1, space_1):
|
def test_reset_inherit(obj_tree_1, space_1):
|
||||||
with scope(space=space_1):
|
with scope(space=space_1):
|
||||||
space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields
|
space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields
|
||||||
parent = obj_tree_1.get_parent()
|
parent = obj_tree_1.get_parent()
|
||||||
child = obj_tree_1.get_descendants()[0]
|
child = obj_tree_1.get_descendants()[0]
|
||||||
obj_tree_1.food_onhand = False
|
obj_tree_1.ignore_shopping = False
|
||||||
assert parent.food_onhand == child.food_onhand
|
assert parent.ignore_shopping == child.ignore_shopping
|
||||||
assert parent.food_onhand != obj_tree_1.food_onhand
|
assert parent.ignore_shopping != obj_tree_1.ignore_shopping
|
||||||
assert parent.supermarket_category != child.supermarket_category
|
assert parent.supermarket_category != child.supermarket_category
|
||||||
assert parent.supermarket_category != obj_tree_1.supermarket_category
|
assert parent.supermarket_category != obj_tree_1.supermarket_category
|
||||||
|
|
||||||
@ -527,5 +527,26 @@ def test_reset_inherit(obj_tree_1, space_1):
|
|||||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||||
parent = obj_tree_1.get_parent()
|
parent = obj_tree_1.get_parent()
|
||||||
child = obj_tree_1.get_descendants()[0]
|
child = obj_tree_1.get_descendants()[0]
|
||||||
assert parent.food_onhand == obj_tree_1.food_onhand == child.food_onhand
|
assert parent.ignore_shopping == obj_tree_1.ignore_shopping == child.ignore_shopping
|
||||||
assert parent.supermarket_category == obj_tree_1.supermarket_category == child.supermarket_category
|
assert parent.supermarket_category == obj_tree_1.supermarket_category == child.supermarket_category
|
||||||
|
|
||||||
|
|
||||||
|
def test_onhand(obj_1, u1_s1, u2_s1):
|
||||||
|
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False
|
||||||
|
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False
|
||||||
|
|
||||||
|
u1_s1.patch(
|
||||||
|
reverse(
|
||||||
|
DETAIL_URL,
|
||||||
|
args={obj_1.id}
|
||||||
|
),
|
||||||
|
{'food_onhand': True},
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == True
|
||||||
|
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False
|
||||||
|
|
||||||
|
user1 = auth.get_user(u1_s1)
|
||||||
|
user2 = auth.get_user(u2_s1)
|
||||||
|
user1.userpreference.shopping_share.add(user2)
|
||||||
|
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == True
|
||||||
|
@ -217,3 +217,6 @@ def test_recent(sle, u1_s1):
|
|||||||
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
|
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
|
||||||
assert len(r) == 10
|
assert len(r) == 10
|
||||||
assert [x['checked'] for x in r].count(False) == 9
|
assert [x['checked'] for x in r].count(False) == 9
|
||||||
|
|
||||||
|
|
||||||
|
# TODO test auto onhand
|
||||||
|
@ -198,11 +198,11 @@ def test_shopping_recipe_userpreference(recipe, sle_count, use_mealplan, user2):
|
|||||||
# setup recipe with 10 ingredients, 1 step recipe with 10 ingredients, 2 food onhand(from recipe and step_recipe)
|
# setup recipe with 10 ingredients, 1 step recipe with 10 ingredients, 2 food onhand(from recipe and step_recipe)
|
||||||
ingredients = Ingredient.objects.filter(step__recipe=recipe)
|
ingredients = Ingredient.objects.filter(step__recipe=recipe)
|
||||||
food = Food.objects.get(id=ingredients[2].food.id)
|
food = Food.objects.get(id=ingredients[2].food.id)
|
||||||
food.food_onhand = True
|
food.onhand_users.add(user)
|
||||||
food.save()
|
food.save()
|
||||||
food = recipe.steps.filter(type=Step.RECIPE).first().step_recipe.steps.first().ingredients.first().food
|
food = recipe.steps.filter(type=Step.RECIPE).first().step_recipe.steps.first().ingredients.first().food
|
||||||
food = Food.objects.get(id=food.id)
|
food = Food.objects.get(id=food.id)
|
||||||
food.food_onhand = True
|
food.onhand_users.add(user)
|
||||||
food.save()
|
food.save()
|
||||||
|
|
||||||
if use_mealplan:
|
if use_mealplan:
|
||||||
|
@ -388,6 +388,7 @@ def user_settings(request):
|
|||||||
up.filter_to_supermarket = shopping_form.cleaned_data['filter_to_supermarket']
|
up.filter_to_supermarket = shopping_form.cleaned_data['filter_to_supermarket']
|
||||||
up.default_delay = shopping_form.cleaned_data['default_delay']
|
up.default_delay = shopping_form.cleaned_data['default_delay']
|
||||||
up.shopping_recent_days = shopping_form.cleaned_data['shopping_recent_days']
|
up.shopping_recent_days = shopping_form.cleaned_data['shopping_recent_days']
|
||||||
|
up.shopping_add_onhand = shopping_form.cleaned_data['shopping_add_onhand']
|
||||||
up.csv_delim = shopping_form.cleaned_data['csv_delim']
|
up.csv_delim = shopping_form.cleaned_data['csv_delim']
|
||||||
up.csv_prefix = shopping_form.cleaned_data['csv_prefix']
|
up.csv_prefix = shopping_form.cleaned_data['csv_prefix']
|
||||||
if up.shopping_auto_sync < settings.SHOPPING_MIN_AUTOSYNC_INTERVAL:
|
if up.shopping_auto_sync < settings.SHOPPING_MIN_AUTOSYNC_INTERVAL:
|
||||||
|
@ -375,6 +375,19 @@
|
|||||||
</em>
|
</em>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col col-md-6">{{ $t("shopping_add_onhand") }}</div>
|
||||||
|
<div class="col col-md-6 text-right">
|
||||||
|
<input type="checkbox" size="sm" v-model="settings.shopping_add_onhand" @change="saveSettings" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row sm mb-3">
|
||||||
|
<div class="col">
|
||||||
|
<em class="small text-muted">
|
||||||
|
{{ $t("shopping_add_onhand_desc") }}
|
||||||
|
</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-md-6">{{ $t("shopping_recent_days") }}</div>
|
<div class="col col-md-6">{{ $t("shopping_recent_days") }}</div>
|
||||||
<div class="col col-md-6 text-right">
|
<div class="col col-md-6 text-right">
|
||||||
@ -590,6 +603,7 @@ export default {
|
|||||||
shopping_recent_days: 7,
|
shopping_recent_days: 7,
|
||||||
csv_delim: ",",
|
csv_delim: ",",
|
||||||
csv_prefix: undefined,
|
csv_prefix: undefined,
|
||||||
|
shopping_add_onhand: true,
|
||||||
},
|
},
|
||||||
new_supermarket: { entrymode: false, value: undefined, editmode: undefined },
|
new_supermarket: { entrymode: false, value: undefined, editmode: undefined },
|
||||||
new_category: { entrymode: false, value: undefined },
|
new_category: { entrymode: false, value: undefined },
|
||||||
|
@ -278,5 +278,7 @@
|
|||||||
"in_shopping": "In Shopping List",
|
"in_shopping": "In Shopping List",
|
||||||
"DelayUntil": "Delay Until",
|
"DelayUntil": "Delay Until",
|
||||||
"mark_complete": "Mark Complete",
|
"mark_complete": "Mark Complete",
|
||||||
"QuickEntry": "Quick Entry"
|
"QuickEntry": "Quick Entry",
|
||||||
|
"shopping_add_onhand_desc": "Mark food 'On Hand' when checked off shopping list.",
|
||||||
|
"shopping_add_onhand": "Auto On Hand"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user