shopping supermarket quick edit

This commit is contained in:
vabene1111
2024-12-01 14:27:08 +01:00
parent fa8cd4a2f0
commit 4149549c88
37 changed files with 116 additions and 41 deletions

View File

@@ -176,29 +176,6 @@ def test_sharing(request, shared, count, sle_2, sle, u1_s1):
assert [x['checked'] for x in r['results']].count(False) == count
def test_completed(sle, u1_s1):
# check 1 entry
u1_s1.patch(reverse(DETAIL_URL, args={sle[0].id}), {'checked': True}, content_type='application/json')
r = json.loads(u1_s1.get(reverse(LIST_URL)).content)
assert r['count'] == 10
# count unchecked entries
assert [x['checked'] for x in r['results']].count(False) == 9
# confirm completed_at is populated
assert [(x['completed_at'] is not None) for x in r['results']
if x['checked']].count(True) == 1
assert json.loads(u1_s1.get(f'{reverse(LIST_URL)}?checked=0').content)['count'] == 9
assert json.loads(u1_s1.get(f'{reverse(LIST_URL)}?checked=1').content)['count'] == 1
# uncheck entry
u1_s1.patch(reverse(DETAIL_URL, args={sle[0].id}), {'checked': False}, content_type='application/json')
r = json.loads(u1_s1.get(reverse(LIST_URL)).content)
assert [x['checked'] for x in r['results']].count(False) == 10
# confirm completed_at value cleared
assert [(x['completed_at'] is not None) for x in r['results']
if x['checked']].count(True) == 0
def test_recent(sle, u1_s1):
user = auth.get_user(u1_s1)
user.userpreference.shopping_recent_days = 7 # hardcoded API limit 14 days

View File

@@ -1445,7 +1445,7 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
if checked:
bulk_entries.update(checked=checked, updated_at=update_timestamp, completed_at=update_timestamp)
else:
bulk_entries.update(checked=checked, updated_at=update_timestamp, completed_at=False)
bulk_entries.update(checked=checked, updated_at=update_timestamp, completed_at=None)
serializer.validated_data['timestamp'] = update_timestamp
# update the onhand for food if shopping_add_onhand is True

View File

@@ -10,9 +10,12 @@
<v-row>
<v-col class="pr-0">
<v-btn height="80px" color="info" density="compact" size="small" block stacked @click="useShoppingStore().delayEntries(entriesList, !isShoppingLineDelayed, true); ">
<v-btn height="80px" color="info" density="compact" size="small" block stacked
@click="useShoppingStore().delayEntries(entriesList, !isShoppingLineDelayed, true); ">
<i class="fa-solid fa-clock-rotate-left fa-2x mb-2"></i>
{{ $t('Postpone') }}
<span v-if="!isShoppingLineDelayed">{{ $t('ShopLater') }}</span>
<span v-if="isShoppingLineDelayed">{{ $t('ShopNow') }}</span>
</v-btn>
</v-col>
<v-col>
@@ -32,7 +35,7 @@
</v-btn>
</v-col>
<v-col class="pt-0">
<v-btn height="80px" color="success" density="compact" size="small" block stacked>
<v-btn height="80px" color="success" density="compact" size="small" @click="addEntryForFood()" block stacked>
<i class="fa-solid fa-plus fa-2x mb-2"></i>
{{ $t('Add') }}
</v-btn>
@@ -66,14 +69,15 @@
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil).toLocaleString(DateTime.DATETIME_SHORT) }}
</v-list-item-subtitle>
<!-- <template #append>-->
<!-- <v-btn size="small" color="delete" icon="$delete" v-if="!e.recipeMealplan">-->
<!-- <v-icon icon="$delete"></v-icon>-->
<!-- </v-btn>-->
<!-- </template>-->
<template #append>
<v-btn size="small" color="edit" icon="$edit" v-if="!e.recipeMealplan">
<v-icon icon="$edit"></v-icon>
<model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id); shoppingListFood.entries.delete(e.id)"
@save="(args: ShoppingListEntry) => (shoppingListFood.entries.set(e.id, args))"></model-edit-dialog>
</v-btn>
</template>
<!-- TODO make properly reactive or delete from the food instance in this component as well | ADD functionality once reactive -->
<model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id!);" v-if="!e.recipeMealplan"></model-edit-dialog>
</v-list-item>
</template>
@@ -140,6 +144,21 @@ function categoryUpdate(category: SupermarketCategory) {
})
}
/**
* add new entry for currently selected food type
*/
function addEntryForFood() {
useShoppingStore().createObject({
food: shoppingListFood.value?.food,
unit: null,
amount: 1,
} as ShoppingListEntry, false).then((r: ShoppingListEntry|undefined) => {
if(r != undefined){
shoppingListFood.value?.entries.set(r.id!, r)
}
})
}
</script>
<style scoped>

View File

@@ -6,6 +6,9 @@
<v-tab value="recipes"><i class="fas fa-book fa-fw"></i> <span class="d-none d-md-block ms-1">{{
$t('Recipes')
}} ({{ useShoppingStore().getAssociatedRecipes().length }})</span></v-tab>
<v-tab value="selected_supermarket" v-if="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null">
<i class="fa-solid fa-store fa-fw"></i> <span class="d-none d-md-block ms-1">{{ useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.name }}</span>
</v-tab>
<v-menu :close-on-content-click="false">
<template v-slot:activator="{ props }">
@@ -180,6 +183,17 @@
</v-container>
</v-window-item>
<v-window-item value="selected_supermarket">
<v-container>
<v-row>
<v-col>
<SupermarketEditor :item="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket"
@save="(args: Supermarket) => (useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = args)"></SupermarketEditor>
</v-col>
</v-row>
</v-container>
</v-window-item>
</v-window>
<shopping-line-item-dialog v-model="shoppingLineItemDialog" v-model:shopping-list-food="shoppingLineItemDialogFood"></shopping-line-item-dialog>
@@ -190,7 +204,7 @@
import {computed, onMounted, ref} from "vue";
import {useShoppingStore} from "@/stores/ShoppingStore";
import {ApiApi, Food, IngredientString, ShoppingListEntry, SupermarketCategory, Unit} from "@/openapi";
import {ApiApi, Food, IngredientString, ShoppingListEntry, Supermarket, SupermarketCategory, Unit} from "@/openapi";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
@@ -199,6 +213,7 @@ import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.
import {IShoppingListCategory, IShoppingListFood, ShoppingGroupingOptions} from "@/types/Shopping";
import {useI18n} from "vue-i18n";
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue";
import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue";
const {t} = useI18n()
@@ -231,8 +246,8 @@ onMounted(() => {
autoSyncLoop()
// refresh selected supermarket since category ordering might have changed
if (Object.keys(useUserPreferenceStore().deviceSettings.shopping_selected_supermarket).length > 0) {
new ApiApi().apiSupermarketRetrieve({id: useUserPreferenceStore().deviceSettings.shopping_selected_supermarket.id!}).then(r => {
if (useUserPreferenceStore().deviceSettings.shopping_selected_supermarket != null) {
new ApiApi().apiSupermarketRetrieve({id: useUserPreferenceStore().deviceSettings.shopping_selected_supermarket!.id!}).then(r => {
useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = r
})
}
@@ -268,10 +283,10 @@ function addIngredient() {
function isCategoryVisible(category: IShoppingListCategory) {
let entryCount = category.stats.countUnchecked
if (useUserPreferenceStore().deviceSettings.shopping_show_checked_entries){
if (useUserPreferenceStore().deviceSettings.shopping_show_checked_entries) {
entryCount += category.stats.countChecked
}
if (useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries){
if (useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries) {
entryCount += category.stats.countUncheckedDelayed
}
return entryCount > 0

View File

@@ -262,6 +262,8 @@
"Servings": "",
"Settings": "",
"Share": "",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "",
"Shopping_Category": "",

View File

@@ -255,6 +255,8 @@
"Servings": "Порции",
"Settings": "Настройки",
"Share": "Споделяне",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Категории за пазаруване",
"Shopping_Category": "Категория за пазаруване",

View File

@@ -334,6 +334,8 @@
"Servings": "",
"Settings": "",
"Share": "",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "",
"ShoppingListEntry": "",
"Shopping_Categories": "",

View File

@@ -332,6 +332,8 @@
"Servings": "Porce",
"Settings": "Nastavení",
"Share": "Sdílet",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Kategorie nákupního seznamu",
"Shopping_Category": "Kategorie nákupního seznamu",

View File

@@ -314,6 +314,8 @@
"Servings": "Serveringer",
"Settings": "Indstillinger",
"Share": "Del",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Indkøbskategorier",
"Shopping_Category": "Indkøbskategori",

View File

@@ -337,6 +337,8 @@
"Servings": "Portionen",
"Settings": "Einstellungen",
"Share": "Teilen",
"ShopLater": "Später kaufen",
"ShopNow": "Jetzt kaufen",
"ShoppingBackgroundSyncWarning": "Schlechte Netzwerkverbindung, Warten auf Synchronisation ...",
"ShoppingListEntry": "Einkaufslisten Eintrag",
"Shopping_Categories": "Einkaufskategorien",

View File

@@ -306,6 +306,8 @@
"Servings": "Μερίδες",
"Settings": "Ρυθμίσεις",
"Share": "Κοινοποίηση",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Κατηγορίες αγορών",
"Shopping_Category": "Κατηγορία αγορών",

View File

@@ -336,6 +336,8 @@
"Servings": "Servings",
"Settings": "Settings",
"Share": "Share",
"ShopLater": "Shop later",
"ShopNow": "Shop now",
"ShoppingBackgroundSyncWarning": "Bad network, waiting to sync ...",
"ShoppingListEntry": "Shoppinglist Entry",
"Shopping_Categories": "Shopping Categories",

View File

@@ -333,6 +333,8 @@
"Servings": "Raciones",
"Settings": "Opciones",
"Share": "Compartir",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "Red defectuosa, esperando para sincronizar ...",
"ShoppingListEntry": "",
"Shopping_Categories": "Categorías Compras",

View File

@@ -187,6 +187,8 @@
"Servings": "Annokset",
"Settings": "Asetukset",
"Share": "Jaa",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Ostoskategoriat",
"Shopping_Category": "Ostosluokka",

View File

@@ -334,6 +334,8 @@
"Servings": "Portions",
"Settings": "Paramètres",
"Share": "Partager",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "Mauvais réseau, en attente de synchronisation ...",
"ShoppingListEntry": "",
"Shopping_Categories": "Catégories de courses",

View File

@@ -335,6 +335,8 @@
"Servings": "מנות",
"Settings": "הגדרות",
"Share": "שיתוף",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "בעיית תקשורת, מחכה לסנכון...",
"ShoppingListEntry": "",
"Shopping_Categories": "קטגוריות קניות",

View File

@@ -308,6 +308,8 @@
"Servings": "Adag",
"Settings": "Beállítások",
"Share": "Megosztás",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Vásárlási kategóriák",
"Shopping_Category": "Vásárlási kategória",

View File

@@ -131,6 +131,8 @@
"Servings": "",
"Settings": "Կարգավորումներ",
"Share": "",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Category": "Գնումների կատեգորիա",
"Shopping_list": "Գնումների ցուցակ",

View File

@@ -284,6 +284,8 @@
"Servings": "Porsi",
"Settings": "Pengaturan",
"Share": "Bagikan",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Kategori Belanja",
"Shopping_Category": "Kategori Belanja",

View File

@@ -334,6 +334,8 @@
"Servings": "",
"Settings": "",
"Share": "",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "",
"ShoppingListEntry": "",
"Shopping_Categories": "",

View File

@@ -292,6 +292,8 @@
"Servings": "Porzioni",
"Settings": "Impostazioni",
"Share": "Condividi",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Categorie di spesa",
"Shopping_Category": "Categoria Spesa",

View File

@@ -312,6 +312,8 @@
"Servings": "",
"Settings": "",
"Share": "",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "",
"Shopping_Category": "",

View File

@@ -304,6 +304,8 @@
"Servings": "Porsjoner",
"Settings": "Innstillinger",
"Share": "Del",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Butikk Kategorier",
"Shopping_Category": "Butikk Kategori",

View File

@@ -308,6 +308,8 @@
"Servings": "Porties",
"Settings": "Instellingen",
"Share": "Deel",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Boodschappen categorieën",
"Shopping_Category": "Boodschappencategorie",

View File

@@ -336,6 +336,8 @@
"Servings": "Porcje",
"Settings": "Ustawienia",
"Share": "Udostępnij",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "Słaba sieć, oczekiwanie na synchronizację...",
"ShoppingListEntry": "",
"Shopping_Categories": "Kategorie zakupów",

View File

@@ -250,6 +250,8 @@
"Servings": "Doses",
"Settings": "Definições",
"Share": "Partilhar",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Categorias de Compras",
"Shopping_Category": "Categoria de Compras",

View File

@@ -323,6 +323,8 @@
"Servings": "Porções",
"Settings": "Configurações",
"Share": "Compartilhar",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "Rede ruim, aguardando sincronização...",
"ShoppingListEntry": "",
"Shopping_Categories": "Categorias de Mercado",

View File

@@ -296,6 +296,8 @@
"Servings": "Porții",
"Settings": "Setări",
"Share": "Împărtășire",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Categorii de cumpărături",
"Shopping_Category": "Categorie de cumpărături",

View File

@@ -235,6 +235,8 @@
"Servings": "Порции",
"Settings": "Настройки",
"Share": "Поделиться",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Категории покупок",
"Shopping_Category": "Категория покупок",

View File

@@ -225,6 +225,8 @@
"Servings": "Porcije",
"Settings": "Nastavitve",
"Share": "Deli",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Kategorije nakupa",
"Shopping_Category": "Kategorija nakupa",

View File

@@ -336,6 +336,8 @@
"Servings": "Portioner",
"Settings": "Inställningar",
"Share": "Dela",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "Dålig uppkoppling, inväntar synkronisering...",
"ShoppingListEntry": "",
"Shopping_Categories": "Shopping kategorier",

View File

@@ -335,6 +335,8 @@
"Servings": "Servis Sayısı",
"Settings": "Ayarlar",
"Share": "Paylaş",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "Kötü bağlantı, senkronizasyon bekleniyor...",
"ShoppingListEntry": "",
"Shopping_Categories": "Alışveriş Kategorileri",

View File

@@ -270,6 +270,8 @@
"Servings": "Порції",
"Settings": "Налаштування",
"Share": "Поділитися",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Shopping_Categories": "Категорії Покупок",
"Shopping_Category": "Категорія Покупок",

View File

@@ -330,6 +330,8 @@
"Servings": "份量",
"Settings": "设置",
"Share": "分享",
"ShopLater": "",
"ShopNow": "",
"ShoppingBackgroundSyncWarning": "网络状况不佳,正在等待进行同步……",
"ShoppingListEntry": "",
"Shopping_Categories": "购物类别",

View File

@@ -103,6 +103,8 @@
"Servings": "",
"Settings": "",
"Share": "",
"ShopLater": "",
"ShopNow": "",
"ShoppingListEntry": "",
"Show_as_header": "顯示為標題",
"Size": "",

View File

@@ -229,8 +229,10 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
if (undo) {
registerChange("CREATE", [r])
}
return r
}).catch((err) => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
return undefined
})
}

View File

@@ -14,9 +14,9 @@ class DeviceSettings {
shopping_show_delayed_entries = false
shopping_show_selected_supermarket_only = false
shopping_selected_grouping = ShoppingGroupingOptions.CATEGORY
shopping_selected_supermarket: Supermarket = {} as Supermarket
shopping_selected_supermarket: Supermarket|null = null
shopping_item_info_created_by = false
shopping_item_info_mealplan = false
shopping_item_info_mealplan = true
shopping_item_info_recipe = true
shopping_show_debug = false