Implement YNAB Export from UI #22
@ -11,6 +11,7 @@
|
|||||||
"@mdi/font": "5.9.55",
|
"@mdi/font": "5.9.55",
|
||||||
"@vueuse/core": "^7.6.1",
|
"@vueuse/core": "^7.6.1",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"pinia": "^2.0.11",
|
"pinia": "^2.0.11",
|
||||||
"postcss": "^8.4.6",
|
"postcss": "^8.4.6",
|
||||||
"tailwindcss": "^3.0.18",
|
"tailwindcss": "^3.0.18",
|
||||||
@ -18,6 +19,7 @@
|
|||||||
"vue-router": "^4.0.12"
|
"vue-router": "^4.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/file-saver": "^2.0.5",
|
||||||
"@vitejs/plugin-vue": "^2.0.0",
|
"@vitejs/plugin-vue": "^2.0.0",
|
||||||
"@vue/cli-plugin-babel": "5.0.0-beta.7",
|
"@vue/cli-plugin-babel": "5.0.0-beta.7",
|
||||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineProps, onMounted, watchEffect } from "vue";
|
import { computed, defineProps, onMounted, ref, watchEffect } from "vue";
|
||||||
import Currency from "../components/Currency.vue";
|
import Currency from "../components/Currency.vue";
|
||||||
import { useBudgetsStore } from "../stores/budget";
|
import { useBudgetsStore } from "../stores/budget";
|
||||||
import { useAccountStore } from "../stores/budget-account";
|
import { useAccountStore } from "../stores/budget-account";
|
||||||
@ -52,6 +52,18 @@ watchEffect(() => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useSessionStore().setTitle("Budget for " + selected.value.Month + "/" + selected.value.Year);
|
useSessionStore().setTitle("Budget for " + selected.value.Month + "/" + selected.value.Year);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const expandedGroups = ref<Map<string, boolean>>(new Map<string, boolean>())
|
||||||
|
|
||||||
|
function toggleGroup(group : {Name : string, Expand: boolean}) {
|
||||||
|
console.log(expandedGroups.value);
|
||||||
|
expandedGroups.value.set(group.Name, !(expandedGroups.value.get(group.Name) ?? group.Expand))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroupState(group : {Name : string, Expand: boolean}) : boolean {
|
||||||
|
return expandedGroups.value.get(group.Name) ?? group.Expand;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -78,8 +90,8 @@ onMounted(() => {
|
|||||||
<th>Available</th>
|
<th>Available</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tbody v-for="group in GroupsForMonth">
|
<tbody v-for="group in GroupsForMonth">
|
||||||
<p class="text-lg font-bold">{{ group }}</p>
|
<a class="text-lg font-bold" @click="toggleGroup(group)">{{ (getGroupState(group) ? "−" : "+") + " " + group.Name }}</a>
|
||||||
<tr v-for="category in GetCategories(group)">
|
<tr v-for="category in GetCategories(group.Name)" v-if="getGroupState(group)">
|
||||||
<td>{{ category.Name }}</td>
|
<td>{{ category.Name }}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
@ -6,6 +6,7 @@ import { useBudgetsStore } from "../stores/budget";
|
|||||||
import { useSessionStore } from "../stores/session";
|
import { useSessionStore } from "../stores/session";
|
||||||
import Card from "../components/Card.vue";
|
import Card from "../components/Card.vue";
|
||||||
import Button from "../components/Button.vue";
|
import Button from "../components/Button.vue";
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
const transactionsFile = ref<File | undefined>(undefined);
|
const transactionsFile = ref<File | undefined>(undefined);
|
||||||
const assignmentsFile = ref<File | undefined>(undefined);
|
const assignmentsFile = ref<File | undefined>(undefined);
|
||||||
@ -15,6 +16,10 @@ onMounted(() => {
|
|||||||
useSessionStore().setTitle("Settings");
|
useSessionStore().setTitle("Settings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const budgetStore = useBudgetsStore();
|
||||||
|
const CurrentBudgetID = computed(() => budgetStore.CurrentBudgetID);
|
||||||
|
const CurrentBudgetName = computed(() => budgetStore.CurrentBudgetName);
|
||||||
|
|
||||||
function gotAssignments(e: Event) {
|
function gotAssignments(e: Event) {
|
||||||
const input = (<HTMLInputElement>e.target);
|
const input = (<HTMLInputElement>e.target);
|
||||||
if (input.files != null)
|
if (input.files != null)
|
||||||
@ -26,19 +31,17 @@ function gotTransactions(e: Event) {
|
|||||||
transactionsFile.value = input.files[0];
|
transactionsFile.value = input.files[0];
|
||||||
};
|
};
|
||||||
function deleteBudget() {
|
function deleteBudget() {
|
||||||
const currentBudgetID = useBudgetsStore().CurrentBudgetID;
|
if (CurrentBudgetID.value == null)
|
||||||
if (currentBudgetID == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DELETE("/budget/" + currentBudgetID);
|
DELETE("/budget/" + CurrentBudgetID.value);
|
||||||
|
|
||||||
const budgetStore = useSessionStore();
|
const budgetStore = useSessionStore();
|
||||||
budgetStore.Budgets.delete(currentBudgetID);
|
budgetStore.Budgets.delete(CurrentBudgetID.value);
|
||||||
useRouter().push("/")
|
useRouter().push("/")
|
||||||
};
|
};
|
||||||
function clearBudget() {
|
function clearBudget() {
|
||||||
const currentBudgetID = useBudgetsStore().CurrentBudgetID;
|
POST("/budget/" + CurrentBudgetID.value + "/settings/clear", null)
|
||||||
POST("/budget/" + currentBudgetID + "/settings/clear", null)
|
|
||||||
};
|
};
|
||||||
function cleanNegative() {
|
function cleanNegative() {
|
||||||
// <a href="/budget/{{.Budget.ID}}/settings/clean-negative">Fix all historic negative category-balances</a>
|
// <a href="/budget/{{.Budget.ID}}/settings/clean-negative">Fix all historic negative category-balances</a>
|
||||||
@ -53,6 +56,22 @@ function ynabImport() {
|
|||||||
const budgetStore = useBudgetsStore();
|
const budgetStore = useBudgetsStore();
|
||||||
budgetStore.ImportYNAB(formData);
|
budgetStore.ImportYNAB(formData);
|
||||||
};
|
};
|
||||||
|
function ynabExport() {
|
||||||
|
const timeStamp = new Date().toISOString();
|
||||||
|
POST("/budget/"+CurrentBudgetID.value+"/export/ynab/assignments", "")
|
||||||
|
.then(x => x.text())
|
||||||
|
.then(x => {
|
||||||
|
var blob = new Blob([x], {type: "text/plain;charset=utf-8"});
|
||||||
|
saveAs(blob, timeStamp + " " + CurrentBudgetName.value + " - Budget.tsv");
|
||||||
|
})
|
||||||
|
POST("/budget/"+CurrentBudgetID.value+"/export/ynab/transactions", "")
|
||||||
|
.then(x => x.text())
|
||||||
|
.then(x => {
|
||||||
|
var blob = new Blob([x], {type: "text/plain;charset=utf-8"});
|
||||||
|
saveAs(blob, timeStamp + " " + CurrentBudgetName.value + " - Transactions.tsv");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -94,6 +113,13 @@ function ynabImport() {
|
|||||||
<Button class="bg-blue-500" :disabled="filesIncomplete" @click="ynabImport">Importieren</Button>
|
<Button class="bg-blue-500" :disabled="filesIncomplete" @click="ynabImport">Importieren</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card class="flex-col p-3">
|
||||||
|
<h2 class="text-lg font-bold">Export as YNAB TSV</h2>
|
||||||
|
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<Button class="bg-blue-500" @click="ynabExport">Export</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
@ -67,7 +67,10 @@ export const useAccountStore = defineStore("budget/account", {
|
|||||||
let prev = undefined;
|
let prev = undefined;
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
if(category.Group != prev)
|
if(category.Group != prev)
|
||||||
categoryGroups.push(category.Group);
|
categoryGroups.push({
|
||||||
|
Name: category.Group,
|
||||||
|
Expand: category.Group != "Hidden Categories",
|
||||||
|
});
|
||||||
prev = category.Group;
|
prev = category.Group;
|
||||||
}
|
}
|
||||||
return categoryGroups;
|
return categoryGroups;
|
||||||
|
@ -1141,6 +1141,11 @@
|
|||||||
"@types/qs" "*"
|
"@types/qs" "*"
|
||||||
"@types/serve-static" "*"
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
|
"@types/file-saver@^2.0.5":
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.5.tgz#9ee342a5d1314bb0928375424a2f162f97c310c7"
|
||||||
|
integrity sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==
|
||||||
|
|
||||||
"@types/glob@^7.1.1":
|
"@types/glob@^7.1.1":
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
|
||||||
@ -4137,6 +4142,11 @@ figures@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
|
|
||||||
|
file-saver@^2.0.5:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
|
||||||
|
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
|
||||||
|
|
||||||
file-uri-to-path@1.0.0:
|
file-uri-to-path@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user