shopping from meal plan edit

This commit is contained in:
vabene1111
2024-12-28 23:26:22 +01:00
parent bf4fc9a7aa
commit c1bfa563a3
45 changed files with 299 additions and 160 deletions

View File

@@ -1155,10 +1155,8 @@ class AutoMealPlanSerializer(serializers.Serializer):
class ShoppingListRecipeSerializer(serializers.ModelSerializer): class ShoppingListRecipeSerializer(serializers.ModelSerializer):
recipe_name = serializers.ReadOnlyField(source='recipe.name') recipe_data = RecipeOverviewSerializer(source='recipe', read_only=True, required=False)
mealplan_note = serializers.ReadOnlyField(source='mealplan.note') meal_plan_data = MealPlanSerializer(source='mealplan', read_only=True, required=False)
mealplan_from_date = serializers.ReadOnlyField(source='mealplan.from_date')
mealplan_type = serializers.ReadOnlyField(source='mealplan.meal_type.name')
servings = CustomDecimalField() servings = CustomDecimalField()
def update(self, instance, validated_data): def update(self, instance, validated_data):
@@ -1170,18 +1168,19 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ShoppingListRecipe model = ShoppingListRecipe
fields = ('id', 'recipe_name', 'name', 'recipe', 'mealplan', 'servings', 'mealplan_note', 'mealplan_from_date', fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings',)
'mealplan_type')
read_only_fields = ('id',) read_only_fields = ('id',)
class ShoppingListEntrySerializer(WritableNestedModelSerializer): class ShoppingListEntrySerializer(WritableNestedModelSerializer):
food = FoodSerializer(allow_null=True) food = FoodSerializer(allow_null=True)
unit = UnitSerializer(allow_null=True, required=False) unit = UnitSerializer(allow_null=True, required=False)
recipe_mealplan = ShoppingListRecipeSerializer(source='list_recipe', read_only=True) list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True)
amount = CustomDecimalField() amount = CustomDecimalField()
created_by = UserSerializer(read_only=True) created_by = UserSerializer(read_only=True)
completed_at = serializers.DateTimeField(allow_null=True, required=False) completed_at = serializers.DateTimeField(allow_null=True, required=False)
mealplan_id = serializers.IntegerField(required=False, write_only=True,
help_text='If a mealplan id is given try to find existing or create new ShoppingListRecipe with that meal plan and link entry to it')
def get_fields(self, *args, **kwargs): def get_fields(self, *args, **kwargs):
fields = super().get_fields(*args, **kwargs) fields = super().get_fields(*args, **kwargs)
@@ -1215,10 +1214,20 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
def create(self, validated_data): def create(self, validated_data):
validated_data['space'] = self.context['request'].space validated_data['space'] = self.context['request'].space
validated_data['created_by'] = self.context['request'].user validated_data['created_by'] = self.context['request'].user
if validated_data['mealplan_id']:
slr, created = ShoppingListRecipe.objects.get_or_create(mealplan_id=validated_data['mealplan_id'], mealplan__space=self.context['request'].space)
validated_data['list_recipe'] = slr
del validated_data['mealplan_id']
return super().create(validated_data) return super().create(validated_data)
def update(self, instance, validated_data): def update(self, instance, validated_data):
user = self.context['request'].user user = self.context['request'].user
if validated_data['mealplan_id']:
del validated_data['mealplan_id']
# update the onhand for food if shopping_add_onhand is True # update the onhand for food if shopping_add_onhand is True
if user.userpreference.shopping_add_onhand: if user.userpreference.shopping_add_onhand:
if checked := validated_data.get('checked', None): if checked := validated_data.get('checked', None):
@@ -1232,8 +1241,7 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
model = ShoppingListEntry model = ShoppingListEntry
fields = ( fields = (
'id', 'list_recipe', 'food', 'unit', 'amount', 'order', 'checked', 'id', 'list_recipe', 'food', 'unit', 'amount', 'order', 'checked',
'recipe_mealplan', 'list_recipe_data', 'created_by', 'created_at', 'updated_at', 'completed_at', 'delay_until', 'mealplan_id'
'created_by', 'created_at', 'updated_at', 'completed_at', 'delay_until'
) )
read_only_fields = ('id', 'created_by', 'created_at') read_only_fields = ('id', 'created_by', 'created_at')

View File

@@ -5,7 +5,7 @@
</template> </template>
<p> <p>
{{ props.text}} {{ props.text}}
<v-btn color="success" class="float-right" v-if="props.actionText != ''" @click="emit('click')">{{ actionText}}</v-btn> <v-btn color="success" class="float-right" v-if="props.actionText" @click="emit('click')">{{ actionText}}</v-btn>
</p> </p>
</v-alert> </v-alert>
</template> </template>

View File

@@ -34,7 +34,7 @@
</v-card> </v-card>
<model-edit-dialog model="MealPlan" v-model="newPlanDialog" :itemDefaults="newPlanDialogDefaultItem" <model-edit-dialog model="MealPlan" v-model="newPlanDialog" :itemDefaults="newPlanDialogDefaultItem" :close-after-create="false"
@create="(arg: any) => useMealPlanStore().plans.set(arg.id, arg)"></model-edit-dialog> @create="(arg: any) => useMealPlanStore().plans.set(arg.id, arg)"></model-edit-dialog>
</v-col> </v-col>
</v-row> </v-row>

View File

@@ -154,7 +154,7 @@ const amounts = computed((): Map<number, ShoppingLineAmount> => {
* compute the second (info) row of the line item based on the entries and the device settings * compute the second (info) row of the line item based on the entries and the device settings
*/ */
const infoRow = computed(() => { const infoRow = computed(() => {
if(props.hideInfoRow){ if (props.hideInfoRow) {
return '' return ''
} }
@@ -171,14 +171,16 @@ const infoRow = computed(() => {
authors.push(e.createdBy.displayName) authors.push(e.createdBy.displayName)
} }
if (e.recipeMealplan !== null) { if (e.listRecipe != null) {
let recipe_name = e.recipeMealplan.recipeName if (e.listRecipeData.recipe != null) {
if (recipes.indexOf(recipe_name) === -1) { let recipe_name = e.listRecipeData.recipeData.name
recipes.push(recipe_name.substring(0, 14) + (recipe_name.length > 14 ? '..' : '')) if (recipes.indexOf(recipe_name) === -1) {
recipes.push(recipe_name.substring(0, 14) + (recipe_name.length > 14 ? '..' : ''))
}
} }
if ('mealplan_from_date' in e.recipeMealplan) { if (e.listRecipeData.mealplan != null) {
let meal_plan_entry = (e?.recipeMealplan?.mealplanType || '') + ' (' + DateTime.fromJSDate(e.recipeMealplan.mealplanFromDate).toLocaleString(DateTime.DATETIME_SHORT) + ')' let meal_plan_entry = (e.listRecipeData.mealPlanData.mealType.name.substring(0, 8) || '') + ' (' + DateTime.fromJSDate(e.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT) + ')'
if (meal_pans.indexOf(meal_plan_entry) === -1) { if (meal_pans.indexOf(meal_plan_entry) === -1) {
meal_pans.push(meal_plan_entry) meal_pans.push(meal_plan_entry)
} }

View File

@@ -83,16 +83,7 @@
</template> </template>
</v-alert> </v-alert>
<v-text-field :label="$t('Shopping_input_placeholder')" density="compact" @keyup.enter="addIngredient()" v-model="ingredientInput" hide-details> <shopping-list-entry-input></shopping-list-entry-input>
<template #append>
<v-btn
density="comfortable"
@click="addIngredient()"
:icon="ingredientInputIcon"
color="create"
></v-btn>
</template>
</v-text-field>
<v-list class="mt-3" density="compact" v-if="!useShoppingStore().initialized"> <v-list class="mt-3" density="compact" v-if="!useShoppingStore().initialized">
<v-skeleton-loader type="list-item"></v-skeleton-loader> <v-skeleton-loader type="list-item"></v-skeleton-loader>
@@ -168,7 +159,7 @@
<v-row> <v-row>
<v-col> <v-col>
<v-card> <v-card>
<v-card-title>{{ $t('Recipes') }}</v-card-title> <v-card-title>{{ $t('Recipes') }} / {{ $t('Meal_Plan') }}</v-card-title>
<v-card-text> <v-card-text>
<v-list> <v-list>
<v-list-item v-for="r in useShoppingStore().getAssociatedRecipes()"> <v-list-item v-for="r in useShoppingStore().getAssociatedRecipes()">
@@ -179,9 +170,14 @@
@confirm="(servings: number) => {updateRecipeServings(r, servings)}"></number-scaler-dialog> @confirm="(servings: number) => {updateRecipeServings(r, servings)}"></number-scaler-dialog>
</v-btn> </v-btn>
</template> </template>
<span class="ms-2">
{{ r.recipeName }} <div class="ms-2">
</span> <p v-if="r.recipe">{{ r.recipeData.name }} <br/></p>
<p v-if="r.mealplan">
{{ r.mealPlanData.mealType.name }} - {{ DateTime.fromJSDate(r.mealPlanData.fromDate).toLocaleString(DateTime.DATE_FULL) }}
</p>
</div>
<template #append> <template #append>
<v-btn icon color="delete"> <v-btn icon color="delete">
<v-icon icon="$delete"></v-icon> <v-icon icon="$delete"></v-icon>
@@ -230,14 +226,13 @@ import {useI18n} from "vue-i18n";
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue"; import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue";
import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue"; import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue";
import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue"; import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue";
import ShoppingListEntryInput from "@/components/inputs/ShoppingListEntryInput.vue";
import {DateTime} from "luxon";
const {t} = useI18n() const {t} = useI18n()
const currentTab = ref("shopping") const currentTab = ref("shopping")
const ingredientInput = ref('')
const ingredientInputIcon = ref('fa-solid fa-plus')
const shoppingLineItemDialog = ref(false) const shoppingLineItemDialog = ref(false)
const shoppingLineItemDialogFood = ref({} as IShoppingListFood) const shoppingLineItemDialogFood = ref({} as IShoppingListFood)
@@ -273,29 +268,6 @@ onMounted(() => {
} }
}) })
/**
* add new ingredient from ingredient text input
*/
function addIngredient() {
const api = new ApiApi()
api.apiIngredientFromStringCreate({ingredientString: {text: ingredientInput.value} as IngredientString}).then(r => {
useShoppingStore().createObject({
amount: Math.max(r.amount, 1),
unit: r.unit,
food: r.food,
} as ShoppingListEntry, true)
ingredientInput.value = ''
ingredientInputIcon.value = 'fa-solid fa-check'
setTimeout(() => {
ingredientInputIcon.value = 'fa-solid fa-plus'
}, 1000)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}
/** /**
* determines if a category as entries that should be visible * determines if a category as entries that should be visible
* @param category * @param category

View File

@@ -0,0 +1,73 @@
<template>
<v-text-field :label="$t('Shopping_input_placeholder')" density="compact" @keyup.enter="addIngredient()" v-model="ingredientInput" :loading="props.loading" hide-details>
<template #append>
<v-btn
density="comfortable"
@click="addIngredient()"
:icon="ingredientInputIcon"
color="create"
></v-btn>
</template>
</v-text-field>
</template>
<script setup lang="ts">
import {PropType, ref} from "vue";
import {ApiApi, IngredientString, MealPlan, ShoppingListEntry, ShoppingListRecipe} from "@/openapi";
import {useShoppingStore} from "@/stores/ShoppingStore";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
const props = defineProps({
shoppingListRecipe: {type: {} as PropType<ShoppingListRecipe>, required: false},
mealPlan: {type: {} as PropType<MealPlan>, required: false},
loading: {type: Boolean, required: false},
})
const ingredientInput = ref('')
const ingredientInputIcon = ref('fa-solid fa-plus')
const loading = ref(false)
/**
* add new ingredient from ingredient text input
*/
function addIngredient() {
const api = new ApiApi()
loading.value = true
api.apiIngredientFromStringCreate({ingredientString: {text: ingredientInput.value} as IngredientString}).then(r => {
let sle = {
amount: Math.max(r.amount, 1),
unit: r.unit,
food: r.food,
} as ShoppingListEntry
console.log('adding SLR ? ', props.mealPlan)
if (props.mealPlan) {
console.log('yes')
sle.mealplanId = props.mealPlan.id
}
useShoppingStore().createObject(sle, true).finally(() => {
loading.value = false
})
ingredientInput.value = ''
ingredientInputIcon.value = 'fa-solid fa-check'
setTimeout(() => {
ingredientInputIcon.value = 'fa-solid fa-plus'
}, 1000)
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
loading.value = false
})
}
</script>
<style scoped>
</style>

View File

@@ -11,7 +11,7 @@
<v-tabs v-model="tab" :disabled="loading" grow> <v-tabs v-model="tab" :disabled="loading" grow>
<v-tab prepend-icon="$mealplan" value="plan">{{ $t('Meal_Plan') }}</v-tab> <v-tab prepend-icon="$mealplan" value="plan">{{ $t('Meal_Plan') }}</v-tab>
<v-tab prepend-icon="$shopping" value="shopping">{{ $t('Shopping_list') }}</v-tab> <v-tab prepend-icon="$shopping" value="shopping" :disabled="!isUpdate()">{{ $t('Shopping_list') }}</v-tab>
</v-tabs> </v-tabs>
<v-card-text> <v-card-text>
@@ -67,26 +67,23 @@
</v-tabs-window-item> </v-tabs-window-item>
<v-tabs-window-item value="shopping"> <v-tabs-window-item value="shopping">
<v-text-field :label="$t('Shopping_input_placeholder')" density="compact" @keyup.enter="addIngredient()" v-model="ingredientInput" hide-details> <closable-help-alert class="mb-2" :text="$t('MealPlanShoppingHelp')"></closable-help-alert>
<template #append>
<v-btn <v-row v-if="isUpdate()" dense style="max-height: 75vh" class="overflow-scroll">
density="comfortable" <v-col>
@click="addIngredient()" <shopping-list-entry-input :loading="useShoppingStore().currentlyUpdating" :meal-plan="editingObj"></shopping-list-entry-input>
:icon="ingredientInputIcon"
color="create" <v-list v-if="editingObj.id">
></v-btn> <shopping-line-item
</template> v-for="slf in useShoppingStore().getMealPlanEntries(editingObj.id)"
</v-text-field> :shopping-list-food="slf"
hide-info-row
></shopping-line-item>
</v-list>
</v-col>
</v-row>
<v-progress-linear class="mt-2" indeterminate v-if="useShoppingStore().currentlyUpdating"></v-progress-linear>
<v-list v-if="editingObj.id">
<shopping-line-item
v-for="slf in useShoppingStore().getMealPlanEntries(editingObj.id)"
:shopping-list-food="slf"
hide-info-row
></shopping-line-item>
</v-list>
</v-tabs-window-item> </v-tabs-window-item>
</v-tabs-window> </v-tabs-window>
@@ -99,7 +96,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {onMounted, PropType, ref} from "vue"; import {onMounted, PropType, ref} from "vue";
import {ApiApi, MealPlan, MealType, ShoppingListEntry} from "@/openapi"; import {ApiApi, MealPlan, MealType, ShoppingListRecipe} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue"; import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions"; import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import {DateTime} from "luxon"; import {DateTime} from "luxon";
@@ -111,8 +108,9 @@ import {VDateInput} from "vuetify/labs/VDateInput";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore";
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue"; import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
import {IShoppingListFood} from "@/types/Shopping";
import {useShoppingStore} from "@/stores/ShoppingStore"; import {useShoppingStore} from "@/stores/ShoppingStore";
import ShoppingListEntryInput from "@/components/inputs/ShoppingListEntryInput.vue";
import ClosableHelpAlert from "@/components/display/ClosableHelpAlert.vue";
const props = defineProps({ const props = defineProps({
item: {type: {} as PropType<MealPlan>, required: false, default: null}, item: {type: {} as PropType<MealPlan>, required: false, default: null},
@@ -125,9 +123,10 @@ const emit = defineEmits(['create', 'save', 'delete', 'close'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, applyItemDefaults, loading, editingObj, modelClass} = useModelEditorFunctions<MealPlan>('MealPlan', emit) const {setupState, deleteObject, saveObject, isUpdate, editingObjName, applyItemDefaults, loading, editingObj, modelClass} = useModelEditorFunctions<MealPlan>('MealPlan', emit)
// object specific data (for selects/display) // object specific data (for selects/display)
const tab = ref('shopping') const tab = ref('plan')
const dateRangeValue = ref([] as Date[]) const dateRangeValue = ref([] as Date[])
const shoppingListRecipe = ref<ShoppingListRecipe | undefined>(undefined)
onMounted(() => { onMounted(() => {
const api = new ApiApi() const api = new ApiApi()
@@ -163,6 +162,12 @@ onMounted(() => {
}, existingItemFunction: () => { }, existingItemFunction: () => {
initializeDateRange() initializeDateRange()
useShoppingStore().refreshFromAPI(editingObj.value.id!) useShoppingStore().refreshFromAPI(editingObj.value.id!)
api.apiShoppingListRecipeList({mealplan: editingObj.value.id!}).then(r => {
if (r.results.length > 0) {
shoppingListRecipe.value = r.results[0]
}
})
} }
},) },)
}) })
@@ -198,6 +203,24 @@ function initializeDateRange() {
} }
} }
/**
* manually create a shopping list recipe (without a recipe) that links manually created entries to the shopping list
*/
function createShoppingListRecipe() {
let api = new ApiApi()
let slr = {
mealplan: editingObj.value.id,
servings: editingObj.value.servings,
} as ShoppingListRecipe
api.apiShoppingListRecipeCreate({shoppingListRecipe: slr}).then(r => {
shoppingListRecipe.value = r
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -4,6 +4,7 @@ import {EditorSupportedModels, GenericModel, getGenericModelFromString} from "@/
import {useI18n} from "vue-i18n"; import {useI18n} from "vue-i18n";
import {ResponseError} from "@/openapi"; import {ResponseError} from "@/openapi";
import {getNestedProperty} from "@/utils/utils"; import {getNestedProperty} from "@/utils/utils";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
// TODO type emit parameter (https://mokkapps.de/vue-tips/emit-event-from-composable) // TODO type emit parameter (https://mokkapps.de/vue-tips/emit-event-from-composable)
// TODO alternatively there seems to be a getContext method to get the calling context (good practice?) // TODO alternatively there seems to be a getContext method to get the calling context (good practice?)
@@ -119,7 +120,13 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
} }
let name = '' let name = ''
if (editingObj.value.id) { if (editingObj.value.id) {
if (useUserPreferenceStore().serverSettings.debug) {
name += '#' + editingObj.value.id
}
modelClass.value.model.toStringKeys.forEach(key => { modelClass.value.model.toStringKeys.forEach(key => {
let value = getNestedProperty(editingObj.value, key) let value = getNestedProperty(editingObj.value, key)
name += ' ' + ((value != null) ? value : '') name += ' ' + ((value != null) ? value : '')

View File

@@ -165,6 +165,7 @@
"Make_Ingredient": "", "Make_Ingredient": "",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "", "Manage_Books": "",
"MealPlanShoppingHelp": "",
"Meal_Plan": "", "Meal_Plan": "",
"Meal_Plan_Days": "", "Meal_Plan_Days": "",
"Meal_Type": "", "Meal_Type": "",

View File

@@ -160,6 +160,7 @@
"Make_Ingredient": "Направете съставка", "Make_Ingredient": "Направете съставка",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Управление на Книги", "Manage_Books": "Управление на Книги",
"MealPlanShoppingHelp": "",
"Meal_Plan": "План на хранене", "Meal_Plan": "План на хранене",
"Meal_Plan_Days": "Бъдещи планове за хранене", "Meal_Plan_Days": "Бъдещи планове за хранене",
"Meal_Type": "Вид хранене", "Meal_Type": "Вид хранене",

View File

@@ -215,6 +215,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Gestioneu els llibres", "Manage_Books": "Gestioneu els llibres",
"Manage_Emails": "", "Manage_Emails": "",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Pla d'àpats", "Meal_Plan": "Pla d'àpats",
"Meal_Plan_Days": "", "Meal_Plan_Days": "",
"Meal_Type": "", "Meal_Type": "",

View File

@@ -214,6 +214,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Spravovat kuchařky", "Manage_Books": "Spravovat kuchařky",
"Manage_Emails": "Spravovat emaily", "Manage_Emails": "Spravovat emaily",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Jídelníček", "Meal_Plan": "Jídelníček",
"Meal_Plan_Days": "Budoucí jídelníčky", "Meal_Plan_Days": "Budoucí jídelníčky",
"Meal_Type": "Druh jídla", "Meal_Type": "Druh jídla",

View File

@@ -201,6 +201,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Administrer bøger", "Manage_Books": "Administrer bøger",
"Manage_Emails": "Håndter Emails", "Manage_Emails": "Håndter Emails",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Madplan", "Meal_Plan": "Madplan",
"Meal_Plan_Days": "Fremtidige madplaner", "Meal_Plan_Days": "Fremtidige madplaner",
"Meal_Type": "Måltidstype", "Meal_Type": "Måltidstype",

View File

@@ -217,6 +217,7 @@
"ManageSubscription": "Tarfi verwalten", "ManageSubscription": "Tarfi verwalten",
"Manage_Books": "Bücher verwalten", "Manage_Books": "Bücher verwalten",
"Manage_Emails": "E-Mails verwalten", "Manage_Emails": "E-Mails verwalten",
"MealPlanShoppingHelp": "Einträge auf der Einkaufsliste können zur besseren Sortierung zu einem Plan gehören. Wird ein Plan mit einem Rezept erstellt, können die Zutaten automatisch auf die Einkaufsliste gesetzt werden (Einstellung).",
"Meal_Plan": "Speiseplan", "Meal_Plan": "Speiseplan",
"Meal_Plan_Days": "Zukünftige Essenspläne", "Meal_Plan_Days": "Zukünftige Essenspläne",
"Meal_Type": "Mahlzeit", "Meal_Type": "Mahlzeit",

View File

@@ -196,6 +196,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Διαχείριση βιβλίων", "Manage_Books": "Διαχείριση βιβλίων",
"Manage_Emails": "Διαχείριση email", "Manage_Emails": "Διαχείριση email",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Πρόγραμμα γευμάτων", "Meal_Plan": "Πρόγραμμα γευμάτων",
"Meal_Plan_Days": "Μελλοντικά προγράμματα γευμάτων", "Meal_Plan_Days": "Μελλοντικά προγράμματα γευμάτων",
"Meal_Type": "Είδος γεύματος", "Meal_Type": "Είδος γεύματος",

View File

@@ -216,6 +216,7 @@
"ManageSubscription": "Manage subscription", "ManageSubscription": "Manage subscription",
"Manage_Books": "Manage Books", "Manage_Books": "Manage Books",
"Manage_Emails": "Manage Emails", "Manage_Emails": "Manage Emails",
"MealPlanShoppingHelp": "Entries on you Shopping List can be related to a Mealplan to sort your list or update/delete them all at once. When creating a Mealplan with a Recipe Shopping List entries for that recipe can be created automatically (setting). ",
"Meal_Plan": "Meal Plan", "Meal_Plan": "Meal Plan",
"Meal_Plan_Days": "Future meal plans", "Meal_Plan_Days": "Future meal plans",
"Meal_Type": "Meal type", "Meal_Type": "Meal type",

View File

@@ -216,6 +216,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Manejar libros", "Manage_Books": "Manejar libros",
"Manage_Emails": "Administrar Correos", "Manage_Emails": "Administrar Correos",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Régimen de comida", "Meal_Plan": "Régimen de comida",
"Meal_Plan_Days": "Planes de comida a futuro", "Meal_Plan_Days": "Planes de comida a futuro",
"Meal_Type": "Tipo de comida", "Meal_Type": "Tipo de comida",

View File

@@ -115,6 +115,7 @@
"Make_Ingredient": "Valmista Ainesosa", "Make_Ingredient": "Valmista Ainesosa",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Hallinnoi kirjoja", "Manage_Books": "Hallinnoi kirjoja",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Ateriasuunnitelma", "Meal_Plan": "Ateriasuunnitelma",
"Meal_Plan_Days": "Tulevat ruokasuunnitelmat", "Meal_Plan_Days": "Tulevat ruokasuunnitelmat",
"Meal_Type": "Ateriatyyppi", "Meal_Type": "Ateriatyyppi",

View File

@@ -215,6 +215,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Gérer les livres", "Manage_Books": "Gérer les livres",
"Manage_Emails": "Gérer les e-mails", "Manage_Emails": "Gérer les e-mails",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Menu de la semaine", "Meal_Plan": "Menu de la semaine",
"Meal_Plan_Days": "Futurs menus", "Meal_Plan_Days": "Futurs menus",
"Meal_Type": "Type de repas", "Meal_Type": "Type de repas",

View File

@@ -216,6 +216,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "נהל ספרים", "Manage_Books": "נהל ספרים",
"Manage_Emails": "נהל כתובות דואר אלקטרוני", "Manage_Emails": "נהל כתובות דואר אלקטרוני",
"MealPlanShoppingHelp": "",
"Meal_Plan": "תוכנית ארוחה", "Meal_Plan": "תוכנית ארוחה",
"Meal_Plan_Days": "תכנון אוכל עתידי", "Meal_Plan_Days": "תכנון אוכל עתידי",
"Meal_Type": "סוג אוכל", "Meal_Type": "סוג אוכל",

View File

@@ -197,6 +197,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Könyvek kezelése", "Manage_Books": "Könyvek kezelése",
"Manage_Emails": "Levelezés kezelése", "Manage_Emails": "Levelezés kezelése",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Menüterv", "Meal_Plan": "Menüterv",
"Meal_Plan_Days": "Jövőbeni menütervek", "Meal_Plan_Days": "Jövőbeni menütervek",
"Meal_Type": "Étkezés", "Meal_Type": "Étkezés",

View File

@@ -84,6 +84,7 @@
"Logout": "", "Logout": "",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Կարգավորել Գրքերը", "Manage_Books": "Կարգավորել Գրքերը",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Ճաշացուցակ", "Meal_Plan": "Ճաշացուցակ",
"Merge": "Միացնել", "Merge": "Միացնել",
"Merge_Keyword": "Միացնել բանալի բառը", "Merge_Keyword": "Միացնել բանալի բառը",

View File

@@ -182,6 +182,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Kelola Buku", "Manage_Books": "Kelola Buku",
"Manage_Emails": "", "Manage_Emails": "",
"MealPlanShoppingHelp": "",
"Meal_Plan": "rencana makan", "Meal_Plan": "rencana makan",
"Meal_Plan_Days": "", "Meal_Plan_Days": "",
"Meal_Type": "", "Meal_Type": "",

View File

@@ -215,6 +215,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "", "Manage_Books": "",
"Manage_Emails": "", "Manage_Emails": "",
"MealPlanShoppingHelp": "",
"Meal_Plan": "", "Meal_Plan": "",
"Meal_Plan_Days": "", "Meal_Plan_Days": "",
"Meal_Type": "", "Meal_Type": "",

View File

@@ -187,6 +187,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Gestisci Libri", "Manage_Books": "Gestisci Libri",
"Manage_Emails": "Gestisci email", "Manage_Emails": "Gestisci email",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Piano alimentare", "Meal_Plan": "Piano alimentare",
"Meal_Plan_Days": "Piani alimentari futuri", "Meal_Plan_Days": "Piani alimentari futuri",
"Meal_Type": "Tipo di pasto", "Meal_Type": "Tipo di pasto",

View File

@@ -199,6 +199,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Tvarkyti knygas", "Manage_Books": "Tvarkyti knygas",
"Manage_Emails": "", "Manage_Emails": "",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Maisto planas", "Meal_Plan": "Maisto planas",
"Meal_Plan_Days": "", "Meal_Plan_Days": "",
"Meal_Type": "", "Meal_Type": "",

View File

@@ -194,6 +194,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Administrer bøker", "Manage_Books": "Administrer bøker",
"Manage_Emails": "Administrer e-poster", "Manage_Emails": "Administrer e-poster",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Måltidsplan", "Meal_Plan": "Måltidsplan",
"Meal_Plan_Days": "Fremtidige måltidsplaner", "Meal_Plan_Days": "Fremtidige måltidsplaner",
"Meal_Type": "Måltidstype", "Meal_Type": "Måltidstype",

View File

@@ -198,6 +198,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Beheer boeken", "Manage_Books": "Beheer boeken",
"Manage_Emails": "E-mail beheren", "Manage_Emails": "E-mail beheren",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Maaltijdplan", "Meal_Plan": "Maaltijdplan",
"Meal_Plan_Days": "Toekomstige maaltijdplannen", "Meal_Plan_Days": "Toekomstige maaltijdplannen",
"Meal_Type": "Maaltype", "Meal_Type": "Maaltype",

View File

@@ -217,6 +217,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Zarządzaj książkami", "Manage_Books": "Zarządzaj książkami",
"Manage_Emails": "Zarządzaj e-mailami", "Manage_Emails": "Zarządzaj e-mailami",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Plan posiłków", "Meal_Plan": "Plan posiłków",
"Meal_Plan_Days": "Przyszłe plany posiłków", "Meal_Plan_Days": "Przyszłe plany posiłków",
"Meal_Type": "Rodzaj posiłku", "Meal_Type": "Rodzaj posiłku",

View File

@@ -157,6 +157,7 @@
"Make_Ingredient": "Fazer ingrediente", "Make_Ingredient": "Fazer ingrediente",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Gerenciar Livros", "Manage_Books": "Gerenciar Livros",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Plano de Refeição", "Meal_Plan": "Plano de Refeição",
"Meal_Plan_Days": "Planos de alimentação futuros", "Meal_Plan_Days": "Planos de alimentação futuros",
"Meal_Type": "Tipo de refeição", "Meal_Type": "Tipo de refeição",

View File

@@ -209,6 +209,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Gerenciar Livros", "Manage_Books": "Gerenciar Livros",
"Manage_Emails": "Gerenciar Emails", "Manage_Emails": "Gerenciar Emails",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Plano de Refeição", "Meal_Plan": "Plano de Refeição",
"Meal_Plan_Days": "Planos de refeição futuros", "Meal_Plan_Days": "Planos de refeição futuros",
"Meal_Type": "Tipo de Comida", "Meal_Type": "Tipo de Comida",

View File

@@ -191,6 +191,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Gestionarea cărților", "Manage_Books": "Gestionarea cărților",
"Manage_Emails": "Gestionarea e-mailurilor", "Manage_Emails": "Gestionarea e-mailurilor",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Plan de alimentare", "Meal_Plan": "Plan de alimentare",
"Meal_Plan_Days": "Planuri de alimentație pe viitor", "Meal_Plan_Days": "Planuri de alimentație pe viitor",
"Meal_Type": "Tipul mesei", "Meal_Type": "Tipul mesei",

View File

@@ -146,6 +146,7 @@
"Make_Ingredient": "Создание инградиента", "Make_Ingredient": "Создание инградиента",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Управление книгами", "Manage_Books": "Управление книгами",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Планирование блюд", "Meal_Plan": "Планирование блюд",
"Meal_Plan_Days": "Планы питания на будущее", "Meal_Plan_Days": "Планы питания на будущее",
"Meal_Type": "Тип питания", "Meal_Type": "Тип питания",

View File

@@ -142,6 +142,7 @@
"Make_Ingredient": "Ustvari sestavino", "Make_Ingredient": "Ustvari sestavino",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Upravljaj knjige", "Manage_Books": "Upravljaj knjige",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Načrt obroka", "Meal_Plan": "Načrt obroka",
"Meal_Plan_Days": "Načrt za prihodnje obroke", "Meal_Plan_Days": "Načrt za prihodnje obroke",
"Meal_Type": "Tip obroka", "Meal_Type": "Tip obroka",

View File

@@ -217,6 +217,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Hantera böcker", "Manage_Books": "Hantera böcker",
"Manage_Emails": "Hantera mejladresser", "Manage_Emails": "Hantera mejladresser",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Måltidsplanering", "Meal_Plan": "Måltidsplanering",
"Meal_Plan_Days": "Framtida måltidsplaner", "Meal_Plan_Days": "Framtida måltidsplaner",
"Meal_Type": "Måltidstyp", "Meal_Type": "Måltidstyp",

View File

@@ -216,6 +216,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Kitapları Yönet", "Manage_Books": "Kitapları Yönet",
"Manage_Emails": "E-postaları Yönet", "Manage_Emails": "E-postaları Yönet",
"MealPlanShoppingHelp": "",
"Meal_Plan": "Yemek Planı", "Meal_Plan": "Yemek Planı",
"Meal_Plan_Days": "Gelecek yemek planları", "Meal_Plan_Days": "Gelecek yemek planları",
"Meal_Type": "Yemek türü", "Meal_Type": "Yemek türü",

View File

@@ -171,6 +171,7 @@
"Make_Ingredient": "", "Make_Ingredient": "",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "Управління Книжкою", "Manage_Books": "Управління Книжкою",
"MealPlanShoppingHelp": "",
"Meal_Plan": "План Харчування", "Meal_Plan": "План Харчування",
"Meal_Plan_Days": "Майбутній план харчування", "Meal_Plan_Days": "Майбутній план харчування",
"Meal_Type": "Тип страви", "Meal_Type": "Тип страви",

View File

@@ -212,6 +212,7 @@
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "管理书籍", "Manage_Books": "管理书籍",
"Manage_Emails": "管理电子邮件", "Manage_Emails": "管理电子邮件",
"MealPlanShoppingHelp": "",
"Meal_Plan": "用餐计划", "Meal_Plan": "用餐计划",
"Meal_Plan_Days": "未来的用餐计划", "Meal_Plan_Days": "未来的用餐计划",
"Meal_Type": "用餐类型", "Meal_Type": "用餐类型",

View File

@@ -69,6 +69,7 @@
"Logout": "", "Logout": "",
"ManageSubscription": "", "ManageSubscription": "",
"Manage_Books": "管理書籍", "Manage_Books": "管理書籍",
"MealPlanShoppingHelp": "",
"Meal_Plan": "膳食計劃", "Meal_Plan": "膳食計劃",
"Messages": "", "Messages": "",
"Miscellaneous": "", "Miscellaneous": "",

View File

@@ -1244,7 +1244,7 @@ export interface ApiShoppingListEntryBulkCreateRequest {
} }
export interface ApiShoppingListEntryCreateRequest { export interface ApiShoppingListEntryCreateRequest {
shoppingListEntry: Omit<ShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'>; shoppingListEntry: Omit<ShoppingListEntry, 'list_recipe_data'|'created_by'|'created_at'|'updated_at'>;
} }
export interface ApiShoppingListEntryDestroyRequest { export interface ApiShoppingListEntryDestroyRequest {
@@ -1260,7 +1260,7 @@ export interface ApiShoppingListEntryListRequest {
export interface ApiShoppingListEntryPartialUpdateRequest { export interface ApiShoppingListEntryPartialUpdateRequest {
id: number; id: number;
patchedShoppingListEntry?: Omit<PatchedShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'>; patchedShoppingListEntry?: Omit<PatchedShoppingListEntry, 'list_recipe_data'|'created_by'|'created_at'|'updated_at'>;
} }
export interface ApiShoppingListEntryRetrieveRequest { export interface ApiShoppingListEntryRetrieveRequest {
@@ -1269,7 +1269,7 @@ export interface ApiShoppingListEntryRetrieveRequest {
export interface ApiShoppingListEntryUpdateRequest { export interface ApiShoppingListEntryUpdateRequest {
id: number; id: number;
shoppingListEntry: Omit<ShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'>; shoppingListEntry: Omit<ShoppingListEntry, 'list_recipe_data'|'created_by'|'created_at'|'updated_at'>;
} }
export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest { export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest {
@@ -1278,7 +1278,7 @@ export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest {
} }
export interface ApiShoppingListRecipeCreateRequest { export interface ApiShoppingListRecipeCreateRequest {
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>; shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_data'|'meal_plan_data'>;
} }
export interface ApiShoppingListRecipeDestroyRequest { export interface ApiShoppingListRecipeDestroyRequest {
@@ -1293,7 +1293,7 @@ export interface ApiShoppingListRecipeListRequest {
export interface ApiShoppingListRecipePartialUpdateRequest { export interface ApiShoppingListRecipePartialUpdateRequest {
id: number; id: number;
patchedShoppingListRecipe?: Omit<PatchedShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>; patchedShoppingListRecipe?: Omit<PatchedShoppingListRecipe, 'recipe_data'|'meal_plan_data'>;
} }
export interface ApiShoppingListRecipeRetrieveRequest { export interface ApiShoppingListRecipeRetrieveRequest {
@@ -1302,7 +1302,7 @@ export interface ApiShoppingListRecipeRetrieveRequest {
export interface ApiShoppingListRecipeUpdateRequest { export interface ApiShoppingListRecipeUpdateRequest {
id: number; id: number;
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>; shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_data'|'meal_plan_data'>;
} }
export interface ApiSpaceListRequest { export interface ApiSpaceListRequest {

View File

@@ -95,7 +95,7 @@ export interface PatchedShoppingListEntry {
* @type {ShoppingListRecipe} * @type {ShoppingListRecipe}
* @memberof PatchedShoppingListEntry * @memberof PatchedShoppingListEntry
*/ */
readonly recipeMealplan?: ShoppingListRecipe; readonly listRecipeData?: ShoppingListRecipe;
/** /**
* *
* @type {User} * @type {User}
@@ -126,6 +126,12 @@ export interface PatchedShoppingListEntry {
* @memberof PatchedShoppingListEntry * @memberof PatchedShoppingListEntry
*/ */
delayUntil?: Date | null; delayUntil?: Date | null;
/**
* If a mealplan id is given try to find existing or create new ShoppingListRecipe with that meal plan and link entry to it
* @type {number}
* @memberof PatchedShoppingListEntry
*/
mealplanId?: number;
} }
/** /**
@@ -152,12 +158,13 @@ export function PatchedShoppingListEntryFromJSONTyped(json: any, ignoreDiscrimin
'amount': json['amount'] == null ? undefined : json['amount'], 'amount': json['amount'] == null ? undefined : json['amount'],
'order': json['order'] == null ? undefined : json['order'], 'order': json['order'] == null ? undefined : json['order'],
'checked': json['checked'] == null ? undefined : json['checked'], 'checked': json['checked'] == null ? undefined : json['checked'],
'recipeMealplan': json['recipe_mealplan'] == null ? undefined : ShoppingListRecipeFromJSON(json['recipe_mealplan']), 'listRecipeData': json['list_recipe_data'] == null ? undefined : ShoppingListRecipeFromJSON(json['list_recipe_data']),
'createdBy': json['created_by'] == null ? undefined : UserFromJSON(json['created_by']), 'createdBy': json['created_by'] == null ? undefined : UserFromJSON(json['created_by']),
'createdAt': json['created_at'] == null ? undefined : (new Date(json['created_at'])), 'createdAt': json['created_at'] == null ? undefined : (new Date(json['created_at'])),
'updatedAt': json['updated_at'] == null ? undefined : (new Date(json['updated_at'])), 'updatedAt': json['updated_at'] == null ? undefined : (new Date(json['updated_at'])),
'completedAt': json['completed_at'] == null ? undefined : (new Date(json['completed_at'])), 'completedAt': json['completed_at'] == null ? undefined : (new Date(json['completed_at'])),
'delayUntil': json['delay_until'] == null ? undefined : (new Date(json['delay_until'])), 'delayUntil': json['delay_until'] == null ? undefined : (new Date(json['delay_until'])),
'mealplanId': json['mealplan_id'] == null ? undefined : json['mealplan_id'],
}; };
} }
@@ -165,7 +172,7 @@ export function PatchedShoppingListEntryToJSON(json: any): PatchedShoppingListEn
return PatchedShoppingListEntryToJSONTyped(json, false); return PatchedShoppingListEntryToJSONTyped(json, false);
} }
export function PatchedShoppingListEntryToJSONTyped(value?: Omit<PatchedShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'> | null, ignoreDiscriminator: boolean = false): any { export function PatchedShoppingListEntryToJSONTyped(value?: Omit<PatchedShoppingListEntry, 'list_recipe_data'|'created_by'|'created_at'|'updated_at'> | null, ignoreDiscriminator: boolean = false): any {
if (value == null) { if (value == null) {
return value; return value;
} }
@@ -181,6 +188,7 @@ export function PatchedShoppingListEntryToJSONTyped(value?: Omit<PatchedShopping
'checked': value['checked'], 'checked': value['checked'],
'completed_at': value['completedAt'] == null ? undefined : ((value['completedAt'] as any).toISOString()), 'completed_at': value['completedAt'] == null ? undefined : ((value['completedAt'] as any).toISOString()),
'delay_until': value['delayUntil'] == null ? undefined : ((value['delayUntil'] as any).toISOString()), 'delay_until': value['delayUntil'] == null ? undefined : ((value['delayUntil'] as any).toISOString()),
'mealplan_id': value['mealplanId'],
}; };
} }

View File

@@ -13,6 +13,21 @@
*/ */
import { mapValues } from '../runtime'; import { mapValues } from '../runtime';
import type { MealPlan } from './MealPlan';
import {
MealPlanFromJSON,
MealPlanFromJSONTyped,
MealPlanToJSON,
MealPlanToJSONTyped,
} from './MealPlan';
import type { RecipeOverview } from './RecipeOverview';
import {
RecipeOverviewFromJSON,
RecipeOverviewFromJSONTyped,
RecipeOverviewToJSON,
RecipeOverviewToJSONTyped,
} from './RecipeOverview';
/** /**
* *
* @export * @export
@@ -25,12 +40,6 @@ export interface PatchedShoppingListRecipe {
* @memberof PatchedShoppingListRecipe * @memberof PatchedShoppingListRecipe
*/ */
id?: number; id?: number;
/**
*
* @type {string}
* @memberof PatchedShoppingListRecipe
*/
readonly recipeName?: string;
/** /**
* *
* @type {string} * @type {string}
@@ -43,36 +52,30 @@ export interface PatchedShoppingListRecipe {
* @memberof PatchedShoppingListRecipe * @memberof PatchedShoppingListRecipe
*/ */
recipe?: number | null; recipe?: number | null;
/**
*
* @type {RecipeOverview}
* @memberof PatchedShoppingListRecipe
*/
readonly recipeData?: RecipeOverview;
/** /**
* *
* @type {number} * @type {number}
* @memberof PatchedShoppingListRecipe * @memberof PatchedShoppingListRecipe
*/ */
mealplan?: number | null; mealplan?: number | null;
/**
*
* @type {MealPlan}
* @memberof PatchedShoppingListRecipe
*/
readonly mealPlanData?: MealPlan;
/** /**
* *
* @type {number} * @type {number}
* @memberof PatchedShoppingListRecipe * @memberof PatchedShoppingListRecipe
*/ */
servings?: number; servings?: number;
/**
*
* @type {string}
* @memberof PatchedShoppingListRecipe
*/
readonly mealplanNote?: string;
/**
*
* @type {Date}
* @memberof PatchedShoppingListRecipe
*/
readonly mealplanFromDate?: Date;
/**
*
* @type {string}
* @memberof PatchedShoppingListRecipe
*/
readonly mealplanType?: string;
} }
/** /**
@@ -93,14 +96,12 @@ export function PatchedShoppingListRecipeFromJSONTyped(json: any, ignoreDiscrimi
return { return {
'id': json['id'] == null ? undefined : json['id'], 'id': json['id'] == null ? undefined : json['id'],
'recipeName': json['recipe_name'] == null ? undefined : json['recipe_name'],
'name': json['name'] == null ? undefined : json['name'], 'name': json['name'] == null ? undefined : json['name'],
'recipe': json['recipe'] == null ? undefined : json['recipe'], 'recipe': json['recipe'] == null ? undefined : json['recipe'],
'recipeData': json['recipe_data'] == null ? undefined : RecipeOverviewFromJSON(json['recipe_data']),
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'], 'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
'mealPlanData': json['meal_plan_data'] == null ? undefined : MealPlanFromJSON(json['meal_plan_data']),
'servings': json['servings'] == null ? undefined : json['servings'], 'servings': json['servings'] == null ? undefined : json['servings'],
'mealplanNote': json['mealplan_note'] == null ? undefined : json['mealplan_note'],
'mealplanFromDate': json['mealplan_from_date'] == null ? undefined : (new Date(json['mealplan_from_date'])),
'mealplanType': json['mealplan_type'] == null ? undefined : json['mealplan_type'],
}; };
} }
@@ -108,7 +109,7 @@ export function PatchedShoppingListRecipeToJSON(json: any): PatchedShoppingListR
return PatchedShoppingListRecipeToJSONTyped(json, false); return PatchedShoppingListRecipeToJSONTyped(json, false);
} }
export function PatchedShoppingListRecipeToJSONTyped(value?: Omit<PatchedShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'> | null, ignoreDiscriminator: boolean = false): any { export function PatchedShoppingListRecipeToJSONTyped(value?: Omit<PatchedShoppingListRecipe, 'recipe_data'|'meal_plan_data'> | null, ignoreDiscriminator: boolean = false): any {
if (value == null) { if (value == null) {
return value; return value;
} }

View File

@@ -95,7 +95,7 @@ export interface ShoppingListEntry {
* @type {ShoppingListRecipe} * @type {ShoppingListRecipe}
* @memberof ShoppingListEntry * @memberof ShoppingListEntry
*/ */
readonly recipeMealplan: ShoppingListRecipe; readonly listRecipeData: ShoppingListRecipe;
/** /**
* *
* @type {User} * @type {User}
@@ -126,6 +126,12 @@ export interface ShoppingListEntry {
* @memberof ShoppingListEntry * @memberof ShoppingListEntry
*/ */
delayUntil?: Date | null; delayUntil?: Date | null;
/**
* If a mealplan id is given try to find existing or create new ShoppingListRecipe with that meal plan and link entry to it
* @type {number}
* @memberof ShoppingListEntry
*/
mealplanId?: number;
} }
/** /**
@@ -134,7 +140,7 @@ export interface ShoppingListEntry {
export function instanceOfShoppingListEntry(value: object): value is ShoppingListEntry { export function instanceOfShoppingListEntry(value: object): value is ShoppingListEntry {
if (!('food' in value) || value['food'] === undefined) return false; if (!('food' in value) || value['food'] === undefined) return false;
if (!('amount' in value) || value['amount'] === undefined) return false; if (!('amount' in value) || value['amount'] === undefined) return false;
if (!('recipeMealplan' in value) || value['recipeMealplan'] === undefined) return false; if (!('listRecipeData' in value) || value['listRecipeData'] === undefined) return false;
if (!('createdBy' in value) || value['createdBy'] === undefined) return false; if (!('createdBy' in value) || value['createdBy'] === undefined) return false;
if (!('createdAt' in value) || value['createdAt'] === undefined) return false; if (!('createdAt' in value) || value['createdAt'] === undefined) return false;
if (!('updatedAt' in value) || value['updatedAt'] === undefined) return false; if (!('updatedAt' in value) || value['updatedAt'] === undefined) return false;
@@ -158,12 +164,13 @@ export function ShoppingListEntryFromJSONTyped(json: any, ignoreDiscriminator: b
'amount': json['amount'], 'amount': json['amount'],
'order': json['order'] == null ? undefined : json['order'], 'order': json['order'] == null ? undefined : json['order'],
'checked': json['checked'] == null ? undefined : json['checked'], 'checked': json['checked'] == null ? undefined : json['checked'],
'recipeMealplan': ShoppingListRecipeFromJSON(json['recipe_mealplan']), 'listRecipeData': ShoppingListRecipeFromJSON(json['list_recipe_data']),
'createdBy': UserFromJSON(json['created_by']), 'createdBy': UserFromJSON(json['created_by']),
'createdAt': (new Date(json['created_at'])), 'createdAt': (new Date(json['created_at'])),
'updatedAt': (new Date(json['updated_at'])), 'updatedAt': (new Date(json['updated_at'])),
'completedAt': json['completed_at'] == null ? undefined : (new Date(json['completed_at'])), 'completedAt': json['completed_at'] == null ? undefined : (new Date(json['completed_at'])),
'delayUntil': json['delay_until'] == null ? undefined : (new Date(json['delay_until'])), 'delayUntil': json['delay_until'] == null ? undefined : (new Date(json['delay_until'])),
'mealplanId': json['mealplan_id'] == null ? undefined : json['mealplan_id'],
}; };
} }
@@ -171,7 +178,7 @@ export function ShoppingListEntryToJSON(json: any): ShoppingListEntry {
return ShoppingListEntryToJSONTyped(json, false); return ShoppingListEntryToJSONTyped(json, false);
} }
export function ShoppingListEntryToJSONTyped(value?: Omit<ShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'> | null, ignoreDiscriminator: boolean = false): any { export function ShoppingListEntryToJSONTyped(value?: Omit<ShoppingListEntry, 'list_recipe_data'|'created_by'|'created_at'|'updated_at'> | null, ignoreDiscriminator: boolean = false): any {
if (value == null) { if (value == null) {
return value; return value;
} }
@@ -187,6 +194,7 @@ export function ShoppingListEntryToJSONTyped(value?: Omit<ShoppingListEntry, 're
'checked': value['checked'], 'checked': value['checked'],
'completed_at': value['completedAt'] == null ? undefined : ((value['completedAt'] as any).toISOString()), 'completed_at': value['completedAt'] == null ? undefined : ((value['completedAt'] as any).toISOString()),
'delay_until': value['delayUntil'] == null ? undefined : ((value['delayUntil'] as any).toISOString()), 'delay_until': value['delayUntil'] == null ? undefined : ((value['delayUntil'] as any).toISOString()),
'mealplan_id': value['mealplanId'],
}; };
} }

View File

@@ -13,6 +13,21 @@
*/ */
import { mapValues } from '../runtime'; import { mapValues } from '../runtime';
import type { MealPlan } from './MealPlan';
import {
MealPlanFromJSON,
MealPlanFromJSONTyped,
MealPlanToJSON,
MealPlanToJSONTyped,
} from './MealPlan';
import type { RecipeOverview } from './RecipeOverview';
import {
RecipeOverviewFromJSON,
RecipeOverviewFromJSONTyped,
RecipeOverviewToJSON,
RecipeOverviewToJSONTyped,
} from './RecipeOverview';
/** /**
* *
* @export * @export
@@ -25,12 +40,6 @@ export interface ShoppingListRecipe {
* @memberof ShoppingListRecipe * @memberof ShoppingListRecipe
*/ */
id?: number; id?: number;
/**
*
* @type {string}
* @memberof ShoppingListRecipe
*/
readonly recipeName: string;
/** /**
* *
* @type {string} * @type {string}
@@ -43,47 +52,39 @@ export interface ShoppingListRecipe {
* @memberof ShoppingListRecipe * @memberof ShoppingListRecipe
*/ */
recipe?: number | null; recipe?: number | null;
/**
*
* @type {RecipeOverview}
* @memberof ShoppingListRecipe
*/
readonly recipeData: RecipeOverview;
/** /**
* *
* @type {number} * @type {number}
* @memberof ShoppingListRecipe * @memberof ShoppingListRecipe
*/ */
mealplan?: number | null; mealplan?: number | null;
/**
*
* @type {MealPlan}
* @memberof ShoppingListRecipe
*/
readonly mealPlanData: MealPlan;
/** /**
* *
* @type {number} * @type {number}
* @memberof ShoppingListRecipe * @memberof ShoppingListRecipe
*/ */
servings: number; servings: number;
/**
*
* @type {string}
* @memberof ShoppingListRecipe
*/
readonly mealplanNote: string;
/**
*
* @type {Date}
* @memberof ShoppingListRecipe
*/
readonly mealplanFromDate: Date;
/**
*
* @type {string}
* @memberof ShoppingListRecipe
*/
readonly mealplanType: string;
} }
/** /**
* Check if a given object implements the ShoppingListRecipe interface. * Check if a given object implements the ShoppingListRecipe interface.
*/ */
export function instanceOfShoppingListRecipe(value: object): value is ShoppingListRecipe { export function instanceOfShoppingListRecipe(value: object): value is ShoppingListRecipe {
if (!('recipeName' in value) || value['recipeName'] === undefined) return false; if (!('recipeData' in value) || value['recipeData'] === undefined) return false;
if (!('mealPlanData' in value) || value['mealPlanData'] === undefined) return false;
if (!('servings' in value) || value['servings'] === undefined) return false; if (!('servings' in value) || value['servings'] === undefined) return false;
if (!('mealplanNote' in value) || value['mealplanNote'] === undefined) return false;
if (!('mealplanFromDate' in value) || value['mealplanFromDate'] === undefined) return false;
if (!('mealplanType' in value) || value['mealplanType'] === undefined) return false;
return true; return true;
} }
@@ -98,14 +99,12 @@ export function ShoppingListRecipeFromJSONTyped(json: any, ignoreDiscriminator:
return { return {
'id': json['id'] == null ? undefined : json['id'], 'id': json['id'] == null ? undefined : json['id'],
'recipeName': json['recipe_name'],
'name': json['name'] == null ? undefined : json['name'], 'name': json['name'] == null ? undefined : json['name'],
'recipe': json['recipe'] == null ? undefined : json['recipe'], 'recipe': json['recipe'] == null ? undefined : json['recipe'],
'recipeData': RecipeOverviewFromJSON(json['recipe_data']),
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'], 'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
'mealPlanData': MealPlanFromJSON(json['meal_plan_data']),
'servings': json['servings'], 'servings': json['servings'],
'mealplanNote': json['mealplan_note'],
'mealplanFromDate': (new Date(json['mealplan_from_date'])),
'mealplanType': json['mealplan_type'],
}; };
} }
@@ -113,7 +112,7 @@ export function ShoppingListRecipeToJSON(json: any): ShoppingListRecipe {
return ShoppingListRecipeToJSONTyped(json, false); return ShoppingListRecipeToJSONTyped(json, false);
} }
export function ShoppingListRecipeToJSONTyped(value?: Omit<ShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'> | null, ignoreDiscriminator: boolean = false): any { export function ShoppingListRecipeToJSONTyped(value?: Omit<ShoppingListRecipe, 'recipe_data'|'meal_plan_data'> | null, ignoreDiscriminator: boolean = false): any {
if (value == null) { if (value == null) {
return value; return value;
} }

View File

@@ -15,6 +15,7 @@ import {
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {isDelayed} from "@/utils/logic_utils"; import {isDelayed} from "@/utils/logic_utils";
import {DateTime} from "luxon";
const _STORE_ID = "shopping_store" const _STORE_ID = "shopping_store"
const UNDEFINED_CATEGORY = 'shopping_undefined_category' const UNDEFINED_CATEGORY = 'shopping_undefined_category'
@@ -157,7 +158,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
let items: IShoppingListFood[] = [] let items: IShoppingListFood[] = []
entries.value.forEach(shoppingListEntry => { entries.value.forEach(shoppingListEntry => {
if (shoppingListEntry.recipeMealplan && shoppingListEntry.recipeMealplan.mealplan == mealPlanId) { if (shoppingListEntry.listRecipe && shoppingListEntry.listRecipeData.mealplan == mealPlanId) {
items.push({ items.push({
food: shoppingListEntry.food, food: shoppingListEntry.food,
entries: new Map<number, ShoppingListEntry>().set(shoppingListEntry.id!, shoppingListEntry) entries: new Map<number, ShoppingListEntry>().set(shoppingListEntry.id!, shoppingListEntry)
@@ -307,8 +308,8 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
let recipes = [] as ShoppingListRecipe[] let recipes = [] as ShoppingListRecipe[]
entries.value.forEach(e => { entries.value.forEach(e => {
if (e.recipeMealplan != null && recipes.findIndex(x => x.id == e.recipeMealplan.id) == -1) { if (e.listRecipe != null && recipes.findIndex(x => x.id == e.listRecipe) == -1) {
recipes.push(e.recipeMealplan) recipes.push(e.listRecipeData)
} }
}) })
@@ -331,8 +332,14 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
groupingKey = entry.food?.supermarketCategory?.name groupingKey = entry.food?.supermarketCategory?.name
} else if (group == ShoppingGroupingOptions.CREATED_BY) { } else if (group == ShoppingGroupingOptions.CREATED_BY) {
groupingKey = entry.createdBy.displayName groupingKey = entry.createdBy.displayName
} else if (group == ShoppingGroupingOptions.RECIPE && entry.recipeMealplan != null) { } else if (group == ShoppingGroupingOptions.RECIPE && entry.listRecipeData != null) {
groupingKey = entry.recipeMealplan.recipeName if (entry.listRecipeData.recipeData != null) {
groupingKey = entry.listRecipeData.recipeData.name
if (entry.listRecipeData.mealPlanData != null) {
groupingKey += ' - ' + entry.listRecipeData.mealPlanData.mealType.name + ' - ' + DateTime.fromJSDate(entry.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT)
}
}
} }
if (!structure.categories.has(groupingKey) && !(group == ShoppingGroupingOptions.CATEGORY && useUserPreferenceStore().deviceSettings.shopping_show_selected_supermarket_only)) { if (!structure.categories.has(groupingKey) && !(group == ShoppingGroupingOptions.CATEGORY && useUserPreferenceStore().deviceSettings.shopping_show_selected_supermarket_only)) {