From dd791b3e322c4dc345fde56c21e5fab0022c881a Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Wed, 11 Oct 2023 23:02:00 -0400 Subject: [PATCH 1/9] fix: improve occurrence checks --- .../Housing/Actions/RegisterOccurrence.php | 4 +- .../Actions/SearchTransactions.php | 13 +- .../js/Components/OccurrenceCheckModal.vue | 122 +++++++++--------- resources/js/Pages/Housing/Occurrence.vue | 22 ++-- resources/js/domains/housing/models/index.ts | 7 +- .../{occurrenceCols.js => occurrenceCols.ts} | 0 6 files changed, 94 insertions(+), 74 deletions(-) rename resources/js/domains/housing/{occurrenceCols.js => occurrenceCols.ts} (100%) diff --git a/app/Domains/Housing/Actions/RegisterOccurrence.php b/app/Domains/Housing/Actions/RegisterOccurrence.php index 0629d905..1e7b1e7c 100644 --- a/app/Domains/Housing/Actions/RegisterOccurrence.php +++ b/app/Domains/Housing/Actions/RegisterOccurrence.php @@ -2,10 +2,10 @@ namespace App\Domains\Housing\Actions; -use App\Domains\Housing\Models\OccurrenceCheck; -use App\Domains\Transaction\Actions\SearchTransactions; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Log; +use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Transaction\Actions\SearchTransactions; class RegisterOccurrence { diff --git a/app/Domains/Transaction/Actions/SearchTransactions.php b/app/Domains/Transaction/Actions/SearchTransactions.php index 4330a879..5da83e26 100644 --- a/app/Domains/Transaction/Actions/SearchTransactions.php +++ b/app/Domains/Transaction/Actions/SearchTransactions.php @@ -3,6 +3,7 @@ namespace App\Domains\Transaction\Actions; use App\Domains\Transaction\Models\Transaction; +use App\Domains\Transaction\Models\TransactionLine; class SearchTransactions { @@ -24,22 +25,26 @@ class SearchTransactions public function __construct() { - $this->modelQuery = Transaction::query(); + $this->modelQuery = TransactionLine::query(); } public function handle(mixed $conditions) { - foreach ($conditions as $field => $condition) { if ($condition) { $parserName = $this->searchConfig[$field]['type']; + if ($field == 'description') { + $field = "concept"; + } \call_user_func([$this, "get$parserName"], $condition, $field); } } - return $this->modelQuery->orderBy('date')->where([ + return $this->modelQuery->orderBy('date')->whereHas('transaction', fn ($q) => $q->where([ 'status' => Transaction::STATUS_VERIFIED, - ])->get(); + ])) + ->whereRaw("(anchor = 1 or is_split =1)") + ->get(); } public function gettext($condition, $field) diff --git a/resources/js/Components/OccurrenceCheckModal.vue b/resources/js/Components/OccurrenceCheckModal.vue index bb5af15a..40e2259a 100644 --- a/resources/js/Components/OccurrenceCheckModal.vue +++ b/resources/js/Components/OccurrenceCheckModal.vue @@ -1,57 +1,4 @@ - - - + + + + diff --git a/resources/js/Pages/Housing/Occurrence.vue b/resources/js/Pages/Housing/Occurrence.vue index 454d0f6e..add065b1 100644 --- a/resources/js/Pages/Housing/Occurrence.vue +++ b/resources/js/Pages/Housing/Occurrence.vue @@ -10,6 +10,8 @@ import CustomTable from '@/Components/atoms/CustomTable.vue'; import OccurrenceCheckModal from '@/Components/OccurrenceCheckModal.vue'; import { occurrenceCols as cols } from '@/domains/housing/occurrenceCols'; +import { OccurrenceItem } from '@/domains/housing/models'; +import { ITransaction } from '@/domains/transactions/models'; defineProps({ occurrence: { @@ -27,15 +29,15 @@ const onSaved = () => { router.reload() } -const addInstance = (id) => { +const addInstance = (id: number) => { router.post(`/housing/occurrence/${id}/instances`) } -const removeLastInstance = (id) => { +const removeLastInstance = (id: number) => { router.delete(`/housing/occurrence/${id}/instances`) } -const handleDelete = (resource) => { +const handleDelete = (resource: OccurrenceItem) => { if (confirm(`Are you sure you want to delete this check ${resource.name}?`)) { router.delete(`/housing/occurrence/${resource.id}`) } @@ -46,7 +48,7 @@ const syncAll = () => { syncForm.post(`/housing/occurrence/sync-all`) } -const handleEdit = (resource) => { +const handleEdit = (resource: OccurrenceItem) => { resourceToEdit.value = resource; isModalOpen.value = true; } @@ -60,7 +62,7 @@ const defaultOptions = { sync: { name: 'sync', label: 'Sync', - handle(resource) { + handle(resource: OccurrenceItem) { router.post(`/housing/occurrence/${resource.id}/sync`) } }, @@ -71,12 +73,13 @@ const defaultOptions = { } } -const options = (row) => { +type IOptionNames = keyof typeof defaultOptions; +const options = () => { return Object.values(defaultOptions).filter(option => !option.hide) }; -const handleOptions = (option, transaction) => { - defaultOptions[option].handle(transaction) +const handleOptions = (optionName: IOptionNames , transaction: ITransaction) => { + defaultOptions[optionName].handle(transaction) }; @@ -102,8 +105,9 @@ const handleOptions = (option, transaction) => { +
-
+
Date: Wed, 11 Oct 2023 23:02:23 -0400 Subject: [PATCH 2/9] fix: improve splits --- resources/js/Components/atoms/InputMoney.vue | 10 +- .../Components/molecules/TransactionCard.vue | 4 +- resources/js/Pages/DashboardDrafts.vue | 9 +- .../js/Pages/System/Notifications/Index.vue | 77 +++++----- .../components/TransactionItems.vue | 142 +++++++++++------- resources/js/utils/calculator.ts | 45 ++---- 6 files changed, 160 insertions(+), 127 deletions(-) diff --git a/resources/js/Components/atoms/InputMoney.vue b/resources/js/Components/atoms/InputMoney.vue index abe9e522..f440f9d9 100644 --- a/resources/js/Components/atoms/InputMoney.vue +++ b/resources/js/Components/atoms/InputMoney.vue @@ -2,14 +2,16 @@ import { useCalculatorInput } from '@/utils/calculator'; // @ts-expect-error: no definitions import { AtInput } from 'atmosphere-ui'; +import { watch } from 'vue'; import { ref, toRefs, nextTick } from 'vue'; const props = defineProps<{ modelValue: string; + history?: string|number[]; disabled?: boolean }>() -const emit = defineEmits(['update:modelValue']); +const emit = defineEmits(['update:modelValue', 'update:history']); const input = ref() const focus = () => { @@ -18,7 +20,7 @@ const focus = () => { const { modelValue } = toRefs(props) -const { calculate } = useCalculatorInput(); +const { calculate, history } = useCalculatorInput(); const onBlur = (evt: InputEvent) => { const result = parseFloat(calculate(evt.target.value)) @@ -28,6 +30,10 @@ const onBlur = (evt: InputEvent) => { } } +watch(() => [...history.value], (historyValue: string[]) => { + emit('update:history', historyValue.join('')) +}) + const emitValue = (value: string) => { if (value !== undefined) { emit('update:modelValue', value) diff --git a/resources/js/Components/molecules/TransactionCard.vue b/resources/js/Components/molecules/TransactionCard.vue index fa5ea726..cdfca923 100644 --- a/resources/js/Components/molecules/TransactionCard.vue +++ b/resources/js/Components/molecules/TransactionCard.vue @@ -126,7 +126,9 @@ const handleOptions = (option: 'remove'|'selected') => { :on-select="handleOptions" @click.stop > - +
diff --git a/resources/js/Pages/DashboardDrafts.vue b/resources/js/Pages/DashboardDrafts.vue index 7e9dc761..d712aada 100644 --- a/resources/js/Pages/DashboardDrafts.vue +++ b/resources/js/Pages/DashboardDrafts.vue @@ -6,7 +6,7 @@ import TransactionsList from '@/domains/transactions/components/TransactionsList.vue'; import { removeTransaction, transactionDBToTransaction, useTransactionModal } from '@/domains/transactions'; import LogerButton from '@/Components/atoms/LogerButton.vue'; -import { useTransactionStore } from '@/store/transactions'; + import { useTransactionStore } from '@/store/transactions'; const transactionsDraft = ref([]); @@ -18,6 +18,13 @@ import { useTransactionStore } from '@/store/transactions'; isLoadingDrafts.value = false }) } + const syncDrafts = async () => { + const url = `/api/finance/transactions/sync-drafts`; + return axios.get(url).then(({ data }) => { + transactionsDraft.value = data; + isLoadingDrafts.value = false + }) + } onMounted(() => { fetchTransactions() diff --git a/resources/js/Pages/System/Notifications/Index.vue b/resources/js/Pages/System/Notifications/Index.vue index 6368d041..7ec20d2f 100644 --- a/resources/js/Pages/System/Notifications/Index.vue +++ b/resources/js/Pages/System/Notifications/Index.vue @@ -1,37 +1,4 @@ - - - + + + diff --git a/resources/js/domains/transactions/components/TransactionItems.vue b/resources/js/domains/transactions/components/TransactionItems.vue index a4311ec3..20cf0387 100644 --- a/resources/js/domains/transactions/components/TransactionItems.vue +++ b/resources/js/domains/transactions/components/TransactionItems.vue @@ -8,27 +8,29 @@ import InputMoney from "@/Components/atoms/InputMoney.vue"; import LogerButton from "@/Components/atoms/LogerButton.vue"; import LogerApiSimpleSelect from "@/Components/organisms/LogerApiSimpleSelect.vue"; import CategoryPicker from "./CategoryPicker.vue"; +import { IAccount, ICategory } from "../models"; +import { formatMoney } from "@/utils"; +import LogerInput from "@/Components/atoms/LogerInput.vue"; + +interface SplitItem { + payee_id: null|number, + payee_label: string, + date: null|Date, + description: string, + category_id: null|number, + counter_account_id: null|number, + account_id: null|number, + amount: number, + history: string|number[] +} -const props = defineProps({ - items: { - type: Array, - default: [], - }, - categories: { - type: Array, - default: [], - }, - accounts: { - type: Array, - default: [], - }, - isTransfer: { - type: Boolean, - }, - fullHeight: { - type: Boolean - } -}); +const props = defineProps<{ + items: SplitItem[], + categories: ICategory[], + accounts: IAccount[], + isTransfer: boolean + fullHeight: boolean +}>(); const accountLabel = computed(() => { return !props.isTransfer ? "Account" : "Source"; }); @@ -47,7 +49,7 @@ const categoryAccounts = computed(() => { return props.isTransfer ? accountsOptions : categoryOptions; }); -const splits = reactive(props.items ?? []); +const splits = reactive(props.items ?? []); const hasSplits = computed(() => splits.length > 1); const defaultRow = { @@ -59,8 +61,15 @@ const defaultRow = { counter_account_id: null, account_id: null, amount: 0, + history: [0] }; +const splitsTotal = computed(() => + splits.reduce((total: number, splitItem: Record): number => { + return total + parseFloat(splitItem.amount ?? 0); + }, 0) +) + const addSplit = () => { splits.push({...defaultRow}); }; @@ -78,7 +87,7 @@ defineExpose({ return splits; }, reset() { - const items = props.items.at?.(0) ?? {} + const items = props.items.at?.(0) ?? structuredClone(defaultRow) splits.splice(0, splits.length, {...items}) } }); @@ -88,12 +97,36 @@ const isPickerOpen = ref(false); diff --git a/resources/js/utils/calculator.ts b/resources/js/utils/calculator.ts index f6c7d2c4..911d2760 100644 --- a/resources/js/utils/calculator.ts +++ b/resources/js/utils/calculator.ts @@ -1,8 +1,6 @@ -import { Ref, toRefs, reactive, onMounted, computed } from 'vue'; +import { toRefs, reactive, computed } from 'vue'; const b = '789456123.0=,DEL,/,x,-,+,CL'; -const buttons = b.slice(0,12).split('') const operators = b.slice(13).split(',') -const changers = ['=','DEL','CL'] export const useCalculatorInput = () => { const state = reactive({ @@ -20,34 +18,13 @@ export const useCalculatorInput = () => { const currentValue = computed(() => { return state.current - }) - - function resolveAction(val) { - (val == '=') ? state.calculate() : this[val]() - } - - function CL() { - state.current = 0 - state.history = [] - state.mode.initial = true - } - - function DEL() { - const len = state.history.length - state.history.pop() - if (state.history.length) { - state.current = 0 - state.last = state.current[0] - state.setMode('deleted', true) - } else { - state.CL() - } - } + }); // Calculate related Functions function calculate(expression: string) { let result = orderExpression(expression) let lastLength = result.length + state.history = [...result]; while (result.length > 1) { for (let i = 0 ; i < result.length; i++) { @@ -78,9 +55,9 @@ export const useCalculatorInput = () => { } function orderExpression(expression = '') { - let history: string[] = state.history + let localHistory: string|number[] = state.history const operations: string[] = [] - history = expression.split('').map((n) => { + localHistory = expression.split('').map((n) => { if (operators.includes(n)){ operations.push(n) return '|' @@ -88,16 +65,16 @@ export const useCalculatorInput = () => { return n }) - const numbers = history.join('').split('|').map(num => parseFloat(num || "0")) - history = []; + const numbers: number[] = localHistory.join('').split('|').map(num => parseFloat(num || "0")) + localHistory = []; - numbers.forEach((n, i) => { - history.push(n) + numbers.forEach((digit: number, i: number) => { + localHistory.push(digit) if (operations[i]) - history.push(operations[i]) + localHistory.push(operations[i]) }) - return history + return localHistory } function doOperation(left: string, operator:string, right: string) { From 4babc66b546129615c0933f3b9755058bade6fa5 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 12 Oct 2023 15:22:34 -0400 Subject: [PATCH 3/9] feat: add frontend --- .../budget/components/BudgetTargetCard.vue | 33 ++- .../budget/components/BudgetTargetForm.vue | 238 +++++++++--------- resources/js/domains/budget/index.ts | 13 +- 3 files changed, 162 insertions(+), 122 deletions(-) diff --git a/resources/js/domains/budget/components/BudgetTargetCard.vue b/resources/js/domains/budget/components/BudgetTargetCard.vue index 0c9feece..d4f0049e 100644 --- a/resources/js/domains/budget/components/BudgetTargetCard.vue +++ b/resources/js/domains/budget/components/BudgetTargetCard.vue @@ -8,19 +8,21 @@ import BudgetProgress from "./BudgetProgress.vue"; import BudgetMoneyLine from "./BudgetMoneyLine.vue"; import { getBudgetTarget } from "@/domains/budget/budgetTotals"; -import { ICategory } from "@/Modules/finance/models/transactions"; -import { BudgetTarget } from "@/Modules/finance/models/budget"; -import { isSpendingTarget } from "@/domains/budget"; +import { isSavingBalance, isSpendingTarget } from "@/domains/budget"; +import { BudgetTarget } from "@/domains/budget/models/budget"; +import { ICategory } from "@/domains/transactions/models"; import { formatDate, formatMoney, formatMonth, toOrdinals } from "@/utils"; +import LogerButton from "@/Components/atoms/LogerButton.vue"; // import IconPicker from '../IconPicker.vue'; const props = defineProps<{ category: ICategory; item: BudgetTarget; editable: boolean; + isProcessing: boolean; }>(); -defineEmits(["edit"]); +defineEmits(["edit", "completed"]); const targetDate = computed(() => { if (props.item.frequency_month_date == -1) { @@ -71,14 +73,27 @@ const fundedLabel = computed(() => isSpendingTarget(props.item) ? 'Funded' : 'Sa const targetAmount = computed(() => { return getBudgetTarget(props.item) }) + +const targetTypeNames = { + saving_balance: 'saving balance' +} + +const getTargetName = (code: string) => { + return targetTypeNames[code] ?? code; +} + +const isGoal = computed(() => { + return isSavingBalance(props.item); +}) + + diff --git a/resources/js/domains/budget/components/BudgetTargetForm.vue b/resources/js/domains/budget/components/BudgetTargetForm.vue index 1d5cdc32..bc9b6a88 100644 --- a/resources/js/domains/budget/components/BudgetTargetForm.vue +++ b/resources/js/domains/budget/components/BudgetTargetForm.vue @@ -1,116 +1,5 @@ - - + + + diff --git a/resources/js/domains/budget/index.ts b/resources/js/domains/budget/index.ts index dbfaa63f..d55a57cd 100644 --- a/resources/js/domains/budget/index.ts +++ b/resources/js/domains/budget/index.ts @@ -1,16 +1,25 @@ +import { BudgetTarget } from "./models/budget" + export * from "./budgetCols" export * from "./useBudget" export * from "./budgetTotals" export * from "./getFrequencyMonthFactor" -export const isSpendingTarget = budgetMetaData => { +export const isSpendingTarget = (budgetMetaData : BudgetTarget) => { return budgetMetaData.target_type == 'spending' } -export const isSavingBalance = budgetMetaData => { +export const isSavingBalance = (budgetMetaData : BudgetTarget) => { return budgetMetaData.target_type == 'saving_balance' } +export enum BudgetTargetTypes { + Spending = 'spending', + SavingBalance = 'saving_balance', + SavingMonthly = 'saving_monthly', + DebtMonthlyPayment = 'saving_monthly', +} + export const targetTypes = [ { value: 'spending', From 6e0f6e9a1c552ef2cd2bea7c04fc910cda0b412a Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 12 Oct 2023 17:07:07 -0400 Subject: [PATCH 4/9] fix: migrations --- .../2022_02_09_013516_automation.php | 5 ++-- .../2023_09_09_231343_budget_credit_cards.php | 4 ++-- .../2023_10_12_204335_version1_stable.php | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 database/migrations/2023_10_12_204335_version1_stable.php diff --git a/database/migrations/2022_02_09_013516_automation.php b/database/migrations/2022_02_09_013516_automation.php index ee51d84f..36cf6ae9 100644 --- a/database/migrations/2022_02_09_013516_automation.php +++ b/database/migrations/2022_02_09_013516_automation.php @@ -1,8 +1,8 @@ json('config')->nullable(); $table->json('track')->nullable(); $table->boolean('status')->default(true); + $table->boolean('is_background')->default(true); $table->timestamps(); }); } diff --git a/database/migrations/2023_09_09_231343_budget_credit_cards.php b/database/migrations/2023_09_09_231343_budget_credit_cards.php index a4919fee..c0c64e86 100644 --- a/database/migrations/2023_09_09_231343_budget_credit_cards.php +++ b/database/migrations/2023_09_09_231343_budget_credit_cards.php @@ -1,8 +1,8 @@ date('completed_at')->nullable(); + }); + + Schema::table('budget_months', function (Blueprint $table) { + $table->decimal('left_from_last_month')->default(0)->after('payments'); + $table->decimal('funded_spending_previous_month')->default(0)->after('left_from_last_month'); + }); + } +}; From 7d88f12f848d8edc60351504597d95ce481c8746 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 12 Oct 2023 17:07:52 -0400 Subject: [PATCH 5/9] fix: improve budget targets --- .../CreatePlannedTransactionsFromBudget.php | 4 +- .../Controllers}/BudgetCategoryController.php | 16 +++--- .../Http/Controllers}/BudgetController.php | 2 +- .../Controllers}/BudgetMonthController.php | 18 +++--- .../Controllers}/BudgetTargetController.php | 12 +++- app/Domains/Budget/Models/BudgetTarget.php | 18 +++++- .../Budget/Services/BudgetCategoryService.php | 27 ++++++--- ...ionService.php => BudgetTargetService.php} | 14 ++++- app/Domains/Budget/routes.php | 21 +++++++ app/Models/Team.php | 5 ++ .../budget/components/BudgetTargetCard.vue | 4 +- .../budget/components/BudgetTargetForm.vue | 9 ++- routes/web.php | 55 +++++++------------ 13 files changed, 132 insertions(+), 73 deletions(-) rename app/{Http/Controllers/Finance => Domains/Budget/Http/Controllers}/BudgetCategoryController.php (99%) rename app/{Http/Controllers/Finance => Domains/Budget/Http/Controllers}/BudgetController.php (95%) rename app/{Http/Controllers/Finance => Domains/Budget/Http/Controllers}/BudgetMonthController.php (97%) rename app/{Http/Controllers/Finance => Domains/Budget/Http/Controllers}/BudgetTargetController.php (69%) rename app/Domains/Budget/Services/{BudgetTargetTransactionService.php => BudgetTargetService.php} (66%) create mode 100644 app/Domains/Budget/routes.php diff --git a/app/Console/Commands/CreatePlannedTransactionsFromBudget.php b/app/Console/Commands/CreatePlannedTransactionsFromBudget.php index e72052c3..f30a9107 100644 --- a/app/Console/Commands/CreatePlannedTransactionsFromBudget.php +++ b/app/Console/Commands/CreatePlannedTransactionsFromBudget.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Domains\Budget\Services\BudgetTargetTransactionService; +use App\Domains\Budget\Services\BudgetTargetService; use App\Models\Team; use Illuminate\Console\Command; @@ -12,7 +12,7 @@ class CreatePlannedTransactionsFromBudget extends Command protected $description = 'Create planned transactions from budget.'; - public function handle(BudgetTargetTransactionService $service): mixed + public function handle(BudgetTargetService $service): mixed { $team = Team::find($this->argument('teamId')); $service->createPlannedTransactions($team->id); diff --git a/app/Http/Controllers/Finance/BudgetCategoryController.php b/app/Domains/Budget/Http/Controllers/BudgetCategoryController.php similarity index 99% rename from app/Http/Controllers/Finance/BudgetCategoryController.php rename to app/Domains/Budget/Http/Controllers/BudgetCategoryController.php index 773e665d..d571e2fc 100644 --- a/app/Http/Controllers/Finance/BudgetCategoryController.php +++ b/app/Domains/Budget/Http/Controllers/BudgetCategoryController.php @@ -1,19 +1,19 @@ back(); } + + public function complete(Category $category, BudgetTarget $budgetTarget, BudgetTargetService $budgetTargetService) + { + $postData = request()->post(); + $budgetTargetService->complete($budgetTarget, $category, $postData); + return redirect()->back(); + } } diff --git a/app/Domains/Budget/Models/BudgetTarget.php b/app/Domains/Budget/Models/BudgetTarget.php index 9a973ef1..df79ccdb 100644 --- a/app/Domains/Budget/Models/BudgetTarget.php +++ b/app/Domains/Budget/Models/BudgetTarget.php @@ -2,16 +2,28 @@ namespace App\Domains\Budget\Models; +use Illuminate\Support\Facades\DB; +use Illuminate\Database\Eloquent\Model; use App\Domains\Transaction\Models\Transaction; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\DB; class BudgetTarget extends Model { use HasFactory; - protected $fillable = ['team_id', 'user_id', 'color', 'amount', 'name', 'target_type', 'frequency', 'frequency_date', 'frequency_week_day', 'frequency_month_date']; + protected $fillable = [ + 'team_id', + 'user_id', + 'color', + 'amount', + 'name', + 'target_type', + 'frequency', + 'frequency_date', + 'frequency_week_day', + 'frequency_month_date', + 'completed_at' + ]; public function getExpensesByPeriod($startDate, $endDate = null) { diff --git a/app/Domains/Budget/Services/BudgetCategoryService.php b/app/Domains/Budget/Services/BudgetCategoryService.php index 56137638..6970d935 100644 --- a/app/Domains/Budget/Services/BudgetCategoryService.php +++ b/app/Domains/Budget/Services/BudgetCategoryService.php @@ -2,18 +2,18 @@ namespace App\Domains\Budget\Services; -use App\Domains\Budget\Data\BudgetReservedNames; -use App\Domains\Budget\Data\CategoryData; -use App\Domains\Budget\Models\BudgetMonth; -use App\Domains\Budget\Models\BudgetTarget; -use App\Events\BudgetAssigned; -use App\Helpers\RequestQueryHelper; use Brick\Money\Money; +use Illuminate\Support\Str; +use App\Events\BudgetAssigned; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; +use App\Helpers\RequestQueryHelper; use Insane\Journal\Models\Core\Account; use Insane\Journal\Models\Core\Category; +use App\Domains\Budget\Data\CategoryData; +use App\Domains\Budget\Models\BudgetMonth; +use App\Domains\Budget\Models\BudgetTarget; +use App\Domains\Budget\Data\BudgetReservedNames; class BudgetCategoryService { @@ -50,6 +50,19 @@ public static function getSavings($teamId, $startDate, $endDate) ->sum(DB::raw('budgeted + activity')); } + public function getLastTransactionMonth($category) + { + return DB::query() + ->where('budget_targets.team_id', $category->team_id) + ->where('budget_targets.category_id', $category->id) + ->where('activity', '<>', 0) + ->whereIn('budget_targets.target_type', ['saving_balance']) + ->from('budget_months') + ->join('budget_targets', 'budget_targets.category_id', 'budget_months.category_id') + ->orderByDesc('month') + ->first(); + } + public function assignBudget(Category $category, string $month, mixed $postData) { $amount = (float) $postData['budgeted']; diff --git a/app/Domains/Budget/Services/BudgetTargetTransactionService.php b/app/Domains/Budget/Services/BudgetTargetService.php similarity index 66% rename from app/Domains/Budget/Services/BudgetTargetTransactionService.php rename to app/Domains/Budget/Services/BudgetTargetService.php index 4d34135f..9cdb471b 100644 --- a/app/Domains/Budget/Services/BudgetTargetTransactionService.php +++ b/app/Domains/Budget/Services/BudgetTargetService.php @@ -2,13 +2,14 @@ namespace App\Domains\Budget\Services; +use App\Domains\AppCore\Models\Category; use App\Domains\Budget\Models\BudgetTarget; use App\Domains\Integration\Concerns\PlannedTransactionDTO; use App\Domains\Transaction\Services\PlannedTransactionService; -class BudgetTargetTransactionService +class BudgetTargetService { - public function __construct(private PlannedTransactionService $plannedService) + public function __construct(private PlannedTransactionService $plannedService, private BudgetCategoryService $budgetCategoryService) { } @@ -36,4 +37,13 @@ private function buildPlanned(BudgetTarget $target, $month) $data = PlannedTransactionDTO::fromTarget($target, $date); $this->plannedService->add($data); } + + public function complete(BudgetTarget $budgetTarget, Category $category, array $postData) { + $budgetTarget->update(array_merge( + $postData, [ + 'completed_at' => $postData['completed_at'] + ?? $this->budgetCategoryService->getLastTransactionMonth($category)?->month, + ])); + + } } diff --git a/app/Domains/Budget/routes.php b/app/Domains/Budget/routes.php new file mode 100644 index 00000000..93c730e3 --- /dev/null +++ b/app/Domains/Budget/routes.php @@ -0,0 +1,21 @@ +group(function () { + Route::resource('/budgets', BudgetCategoryController::class); + Route::controller(BudgetTargetController::class)->group(function () { + Route::post('/budgets/{category}/targets/', 'store')->name('budget.target.store'); + Route::put('/budgets/{category}/targets/{budgetTarget}', 'update')->name('budget.target.update'); + Route::post('/budgets/{category}/targets/{budgetTarget}', 'complete')->name('budget-target.complete'); + }); + Route::controller(BudgetMonthController::class)->group(function () { + Route::post('/budgets/{category}/months/{month}', 'assign')->name('budget.assignment'); + Route::put('/budgets/{category}/months/{month}', 'updateActivity')->name('budget.update-activity'); + Route::post('/budgets-import', 'import')->name('budget.import'); + Route::get('/budgets-export', 'export')->name('budget.export'); + }); +}); diff --git a/app/Models/Team.php b/app/Models/Team.php index 7b8381ea..6e3e4bad 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -59,6 +59,11 @@ public function settings() return $this->hasMany(Setting::class); } + public function timezone() + { + return $this->hasOne(Setting::class)->where(['name' => 'team_timezone']); + } + public function accounts() { return $this->hasMany(Account::class); diff --git a/resources/js/domains/budget/components/BudgetTargetCard.vue b/resources/js/domains/budget/components/BudgetTargetCard.vue index d4f0049e..8101dc44 100644 --- a/resources/js/domains/budget/components/BudgetTargetCard.vue +++ b/resources/js/domains/budget/components/BudgetTargetCard.vue @@ -132,8 +132,8 @@ const isGoal = computed(() => { -
- +
+ Complete Goal
diff --git a/resources/js/domains/budget/components/BudgetTargetForm.vue b/resources/js/domains/budget/components/BudgetTargetForm.vue index bc9b6a88..cd3bc124 100644 --- a/resources/js/domains/budget/components/BudgetTargetForm.vue +++ b/resources/js/domains/budget/components/BudgetTargetForm.vue @@ -117,7 +117,10 @@ const onCancel = () => { const formComplete = useForm({}) const markAsComplete = (item: BudgetTarget) => { - formComplete.post(route('target.complete', item)) + formComplete.post(route('budget-target.complete', { + category: props.category, + budgetTarget: item + })) } const { selectedSpan } = useDatePager({ nextMode: "month" }); @@ -265,12 +268,12 @@ const handleOptions = (option: string) => { Cancel
- Save - +
diff --git a/routes/web.php b/routes/web.php index ab8b2e08..5f86cc79 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,36 +1,36 @@ group(function () { Route::get('/', fn () => redirect('/dashboard')); @@ -113,20 +114,6 @@ Route::get('/finance', 'index')->name('finance.overview'); }); - // Budgeting & Goals routes - // Route::get('/budgets', [BudgetController::class, 'index']); - Route::resource('/budgets', BudgetCategoryController::class); - Route::controller(BudgetTargetController::class)->group(function () { - Route::post('/budgets/{category}/targets/', 'store')->name('budget.target.store'); - Route::put('/budgets/{category}/targets/{budgetTarget}', 'update')->name('budget.target.update'); - }); - Route::controller(BudgetMonthController::class)->group(function () { - Route::post('/budgets/{category}/months/{month}', 'assign')->name('budget.assignment'); - Route::put('/budgets/{category}/months/{month}', 'updateActivity')->name('budget.update-activity'); - Route::post('/budgets-import', 'import')->name('budget.import'); - Route::get('/budgets-export', 'export')->name('budget.export'); - }); - // Accounts Route::resource('/finance/accounts', FinanceAccountController::class, [ 'as' => 'finance', From 398c407d4eece515c051dcede76e63e857ee624e Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 12 Oct 2023 21:10:07 -0400 Subject: [PATCH 6/9] feat: add mark as completed --- app/Domains/AppCore/Models/Category.php | 9 ++++++--- .../Controllers/BudgetCategoryController.php | 20 ++++++++++++------- .../Models/Traits/BudgetCategoryTrait.php | 20 +++++++++++++++++++ .../Budget/Services/BudgetCategoryService.php | 3 +-- .../Budget/Services/BudgetMonthService.php | 2 +- .../Controllers/Finance/FinanceController.php | 2 +- app/Http/Resources/CategoryCollection.php | 16 ++++++--------- 7 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 app/Domains/Budget/Models/Traits/BudgetCategoryTrait.php diff --git a/app/Domains/AppCore/Models/Category.php b/app/Domains/AppCore/Models/Category.php index 3cf7b279..8cf6a580 100644 --- a/app/Domains/AppCore/Models/Category.php +++ b/app/Domains/AppCore/Models/Category.php @@ -2,13 +2,16 @@ namespace App\Domains\AppCore\Models; +use App\Models\Team; use App\Domains\Budget\Models\BudgetMonth; use App\Domains\Budget\Models\BudgetTarget; -use App\Models\Team; use Insane\Journal\Models\Core\Category as CoreCategory; +use App\Domains\Budget\Models\Traits\BudgetCategoryTrait; class Category extends CoreCategory { + use BudgetCategoryTrait; + protected $with = ['budget']; public function team() @@ -23,7 +26,7 @@ public function budget() public function budgets() { - return $this->hasMany(BudgetMonth::class)->orderBy('month', 'desc'); + return $this->hasMany(BudgetMonth::class)->orderBy('month', 'desc')->limit(2); } public function subCategories() @@ -33,6 +36,6 @@ public function subCategories() public function lastMonthBudget() { - return $this->hasMany(BudgetMonth::class)->orderBy('month', 'desc')->limit(1); + return $this->hasOne(BudgetMonth::class)->orderBy('month', 'desc')->limit(1); } } diff --git a/app/Domains/Budget/Http/Controllers/BudgetCategoryController.php b/app/Domains/Budget/Http/Controllers/BudgetCategoryController.php index d571e2fc..45fe581f 100644 --- a/app/Domains/Budget/Http/Controllers/BudgetCategoryController.php +++ b/app/Domains/Budget/Http/Controllers/BudgetCategoryController.php @@ -34,7 +34,7 @@ public function __construct(Category $category, public BudgetAccountService $acc 'name' => 'required|string|max:255|unique:categories', ]; $this->sorts = ['index']; - $this->includes = ['subCategories', 'subCategories.budget', 'subCategories.budgets']; + $this->includes = []; $this->filters = [ 'parent_id' => '$null', 'resource_type' => 'transactions', @@ -42,19 +42,25 @@ public function __construct(Category $category, public BudgetAccountService $acc $this->resourceName = 'budgets'; } - protected function getIndexProps(Request $request, $resources = null): array - { + protected function index(Request $request) { + $resourceName = $this->resourceName ?? $this->model->getTable(); $queryParams = request()->query(); $settings = Setting::getByTeam(auth()->user()->current_team_id); $timeZone = $settings['team_timezone'] ?? config('app.timezone'); $filters = isset($queryParams['filter']) ? $queryParams['filter'] : []; [$startDate, $endDate] = $this->getFilterDates($filters, $timeZone); - $accountTotalBalance = $this->accountService->getBalanceAs($request->user()->current_team_id, $endDate); + $resources = $this->parser($this->getModelQuery($request, null, function ($model) use ($startDate) { + return $model->withCurrentSavings($startDate); + })); - return [ - 'accountTotal' => $accountTotalBalance, - ]; + + return inertia($this->templates['index'], + [ + $resourceName => $this->parser($resources), + "serverSearchOptions" => $this->getServerParams(), + "accountTotal" => $this->accountService->getBalanceAs($request->user()->current_team_id, $endDate) + ]); } public function show(int $categoryId) diff --git a/app/Domains/Budget/Models/Traits/BudgetCategoryTrait.php b/app/Domains/Budget/Models/Traits/BudgetCategoryTrait.php new file mode 100644 index 00000000..69119fbb --- /dev/null +++ b/app/Domains/Budget/Models/Traits/BudgetCategoryTrait.php @@ -0,0 +1,20 @@ +with(['subCategories' => fn ($q) => $q->whereHas('budget', function($query) use ($startDate) { + return $query->whereRaw('(completed_at is null or completed_at > ?)', $startDate); + })->orWhereDoesntHave('budget')]); + } + + /*** + * here will the filters for: + * go funded + * not funded + * credit cards + * savings or goals + * */ + + +} diff --git a/app/Domains/Budget/Services/BudgetCategoryService.php b/app/Domains/Budget/Services/BudgetCategoryService.php index 6970d935..9622b013 100644 --- a/app/Domains/Budget/Services/BudgetCategoryService.php +++ b/app/Domains/Budget/Services/BudgetCategoryService.php @@ -36,9 +36,8 @@ public function updateTarget(Category $category, mixed $postData) ])); } - public static function getSavings($teamId, $startDate, $endDate) + public static function getSavingsBalance($teamId, $startDate, $endDate) { - $startMonth = substr((string) $startDate, 0, 7); $endMonth = substr((string) $endDate, 0, 7); return DB::query() diff --git a/app/Domains/Budget/Services/BudgetMonthService.php b/app/Domains/Budget/Services/BudgetMonthService.php index f395c732..429fd1b4 100644 --- a/app/Domains/Budget/Services/BudgetMonthService.php +++ b/app/Domains/Budget/Services/BudgetMonthService.php @@ -29,7 +29,7 @@ public function updateTarget(Category $category, mixed $postData) ])); } - public static function getSavings($teamId, $startDate, $endDate) + public static function getSavingsBalance($teamId, $startDate, $endDate) { $startMonth = substr((string) $startDate, 0, 7); $endMonth = substr((string) $endDate, 0, 7); diff --git a/app/Http/Controllers/Finance/FinanceController.php b/app/Http/Controllers/Finance/FinanceController.php index 8ad2433d..334dd050 100644 --- a/app/Http/Controllers/Finance/FinanceController.php +++ b/app/Http/Controllers/Finance/FinanceController.php @@ -48,7 +48,7 @@ public function index(Request $request) $lastMonthExpenses = TransactionService::getExpensesTotal($teamId, $lastMonthStartDate, $lastMonthEndDate); $income = TransactionService::getIncome($teamId, $startDate, $endDate); $lastMonthIncome = TransactionService::getIncome($teamId, $lastMonthStartDate, $lastMonthEndDate); - $savings = BudgetCategoryService::getSavings($teamId, $startDate, $endDate); + $savings = BudgetCategoryService::getSavingsBalance($teamId, $startDate, $endDate); return Jetstream::inertia()->render($request, 'Finance/Index', [ 'sectionTitle' => 'Finance', diff --git a/app/Http/Resources/CategoryCollection.php b/app/Http/Resources/CategoryCollection.php index 710a24ef..e93cc3f8 100644 --- a/app/Http/Resources/CategoryCollection.php +++ b/app/Http/Resources/CategoryCollection.php @@ -2,9 +2,9 @@ namespace App\Http\Resources; -use App\Domains\Budget\Services\BudgetCategoryService; use App\Helpers\RequestQueryHelper; use Illuminate\Http\Resources\Json\JsonResource; +use App\Domains\Budget\Services\BudgetCategoryService; class CategoryCollection extends JsonResource { @@ -25,14 +25,10 @@ public function toArray($request) $month = $startDate ?? date('Y-m-01'); $normalArray = parent::toArray($request); - // if ($this->id == 723) { - // dd( $this->service->getBudgetInfo($this->resource, $month), $this->id); - // } - return array_merge( - $normalArray, - [ - 'month' => $month, - ], - $this->service->getBudgetInfo($this->resource, $month)); + return [ + ...$normalArray, + ...$this->service->getBudgetInfo($this->resource, $month), + 'month' => $month + ]; } } From 2bd33d271f07b7a58f985219c8c97a669511c3f0 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Thu, 12 Oct 2023 21:11:23 -0400 Subject: [PATCH 7/9] fix: remove occurrence check debugger --- resources/js/Components/OccurrenceCheckModal.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/Components/OccurrenceCheckModal.vue b/resources/js/Components/OccurrenceCheckModal.vue index 40e2259a..57c0a2d8 100644 --- a/resources/js/Components/OccurrenceCheckModal.vue +++ b/resources/js/Components/OccurrenceCheckModal.vue @@ -89,7 +89,7 @@ const submit = () => { }; const update = () => { - debugger + form.put(route('occurrence.update', props.formData), { onSuccess() { emitClose(); From a85e3e92258577e18064f64a68d1bcc4cf169569 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Fri, 13 Oct 2023 22:02:40 -0400 Subject: [PATCH 8/9] fix: Include last 12 month Net Worth --- .../Services/TransactionService.php | 15 +++++---- .../Finance/FinanceTrendController.php | 16 ++++----- .../js/Components/ChartHeaderScroller.vue | 33 +++++++++++++++---- resources/js/Components/ChartNetworth.vue | 2 +- resources/js/Pages/Finance/Trends.vue | 29 ++-------------- 5 files changed, 45 insertions(+), 50 deletions(-) diff --git a/app/Domains/Transaction/Services/TransactionService.php b/app/Domains/Transaction/Services/TransactionService.php index bcf4b68d..8a341790 100644 --- a/app/Domains/Transaction/Services/TransactionService.php +++ b/app/Domains/Transaction/Services/TransactionService.php @@ -2,14 +2,14 @@ namespace App\Domains\Transaction\Services; -use App\Domains\Budget\Data\BudgetReservedNames; -use App\Domains\Transaction\Imports\TransactionsImport; -use App\Domains\Transaction\Models\Transaction; -use App\Domains\Transaction\Models\TransactionLine; -use Brick\Math\RoundingMode; use Brick\Money\Money; +use Brick\Math\RoundingMode; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; +use App\Domains\Transaction\Models\Transaction; +use App\Domains\Budget\Data\BudgetReservedNames; +use App\Domains\Transaction\Models\TransactionLine; +use App\Domains\Transaction\Imports\TransactionsImport; class TransactionService { @@ -229,7 +229,7 @@ public static function getNetWorth($teamId, $startDate, $endDate) INNER JOIN transactions t on tl.transaction_id = t.id INNER JOIN accounts on tl.account_id = accounts.id INNER JOIN account_detail_types adt on adt.id = accounts.account_detail_type_id - WHERE t.STATUS = 'verified' + WHERE t.STATUS = 'verified' AND tl.date <= :monthDate AND adt.name IN ('cash', 'cash_on_hand', 'bank', 'savings', 'credit_card') AND tl.team_id = :teamId AND balance_type IS NOT null @@ -239,10 +239,11 @@ public static function getNetWorth($teamId, $startDate, $endDate) SUM(SUM(CASE WHEN balance_type = 'credit' THEN total ELSE 0 END)) over (ORDER BY month_date) as debts FROM DATA GROUP BY month_date - ORDER BY month_date + ORDER BY month_date DESC LIMIT 12; ", [ 'teamId' => $teamId, + 'monthDate' => $endDate // 'detailTypes' => implode(',', AccountDetailType::ALL) ]); } diff --git a/app/Http/Controllers/Finance/FinanceTrendController.php b/app/Http/Controllers/Finance/FinanceTrendController.php index e267e8b0..617be954 100644 --- a/app/Http/Controllers/Finance/FinanceTrendController.php +++ b/app/Http/Controllers/Finance/FinanceTrendController.php @@ -2,12 +2,12 @@ namespace App\Http\Controllers\Finance; -use App\Domains\Transaction\Services\ReportService; -use App\Domains\Transaction\Services\TransactionService; -use App\Http\Controllers\Controller; use Carbon\Carbon; -use Freesgen\Atmosphere\Http\Querify; use Illuminate\Http\Request; +use App\Http\Controllers\Controller; +use Freesgen\Atmosphere\Http\Querify; +use App\Domains\Transaction\Services\ReportService; +use App\Domains\Transaction\Services\TransactionService; class FinanceTrendController extends Controller { @@ -67,12 +67,12 @@ public function category(Request $request) $queryParams = $request->query(); $filters = isset($queryParams['filter']) ? $queryParams['filter'] : []; [$startDate, $endDate] = $this->getFilterDates($filters); - $teamId = $request->user()->current_team_id; + $parentId = $filters['parent_id'] ?? null; - $data = TransactionService::getCategoryExpenses($teamId, $startDate, $endDate, null, $filters['parent_id'] ?? null); + $data = TransactionService::getCategoryExpenses($teamId, $startDate, $endDate, null, $parentId); $hasData = isset($data[0]); - $parentName = $hasData ? $data[0]?->parent_name.' - ' : null; + $parentName = $hasData && $parentId ? $data[0]?->parent_name.' - ' : null; return [ 'data' => $data, @@ -111,7 +111,7 @@ public function netWorth(Request $request) $teamId = $request->user()->current_team_id; return [ - 'data' => TransactionService::getNetWorth($teamId, $startDate, $endDate), + 'data' => collect(TransactionService::getNetWorth($teamId, $startDate, $endDate))->reverse()->values(), 'metaData' => [ 'name' => 'netWorth', 'title' => 'Net Worth', diff --git a/resources/js/Components/ChartHeaderScroller.vue b/resources/js/Components/ChartHeaderScroller.vue index 0b60bebb..37414833 100644 --- a/resources/js/Components/ChartHeaderScroller.vue +++ b/resources/js/Components/ChartHeaderScroller.vue @@ -1,6 +1,6 @@ From f3557d868f921561ebf4a88d7e9e9f91ced23d1c Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Sat, 14 Oct 2023 01:10:48 -0400 Subject: [PATCH 9/9] fix: improve occurrence checks and import --- .../Commands/MakeOccurrenceReminders.php | 6 +- .../Services/LogerAutomationService.php | 16 +-- .../Housing/Actions/RegisterOccurrence.php | 93 +++++++------- app/Domains/Housing/Data/OccurrenceData.php | 38 ++++++ .../Housing/Exports/OccurrenceExport.php | 38 +++++- .../Http/Controllers/OccurrenceController.php | 37 +++--- .../Http/Controllers/ProjectController.php | 4 +- .../Housing/Imports/OccurrenceImport.php | 29 +++++ .../{OccurrenceCheck.php => Occurrence.php} | 24 +++- app/Domains/Housing/routes.php | 25 ++-- .../Actions/OccurrenceCheckAutomation.php | 2 +- .../LogerProfile/Data/ProfileEntityType.php | 6 +- app/Events/OccurrenceCreated.php | 4 +- app/Jobs/RunTeamChecks.php | 4 +- app/Listeners/TrashTeamSettings.php | 4 +- app/Notifications/OccurrenceAlert.php | 6 +- components.d.ts | 4 + resources/js/Components/AppUserMenu.vue | 12 +- .../js/Components/ImportResourceModal.vue | 116 +++++++++++------- resources/js/Components/LogerProfileModal.vue | 2 +- resources/js/Components/OccurrencePreview.vue | 4 +- .../js/Components/organisms/ImportHolder.vue | 80 +++++++----- resources/js/Pages/Finance/Category.vue | 4 +- resources/js/Pages/Housing/Equipment.vue | 6 +- resources/js/Pages/Housing/Occurrence.vue | 12 +- 25 files changed, 387 insertions(+), 189 deletions(-) create mode 100644 app/Domains/Housing/Data/OccurrenceData.php create mode 100644 app/Domains/Housing/Imports/OccurrenceImport.php rename app/Domains/Housing/Models/{OccurrenceCheck.php => Occurrence.php} (82%) diff --git a/app/Console/Commands/MakeOccurrenceReminders.php b/app/Console/Commands/MakeOccurrenceReminders.php index fe1b51d5..286302db 100644 --- a/app/Console/Commands/MakeOccurrenceReminders.php +++ b/app/Console/Commands/MakeOccurrenceReminders.php @@ -3,7 +3,7 @@ namespace App\Console\Commands; use App\Domains\Housing\Contracts\OccurrenceNotifyTypes; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use App\Models\User; use App\Notifications\OccurrenceAlert; use Illuminate\Console\Command; @@ -31,8 +31,8 @@ class MakeOccurrenceReminders extends Command */ public function handle() { - $occurrencesOnLast = OccurrenceCheck::getForNotificationType(OccurrenceNotifyTypes::LAST); - $occurrencesOnAvg = OccurrenceCheck::getForNotificationType(OccurrenceNotifyTypes::AVG); + $occurrencesOnLast = Occurrence::getForNotificationType(OccurrenceNotifyTypes::LAST); + $occurrencesOnAvg = Occurrence::getForNotificationType(OccurrenceNotifyTypes::AVG); $this->sendNotifications($occurrencesOnLast); $this->sendNotifications($occurrencesOnAvg); diff --git a/app/Domains/Automation/Services/LogerAutomationService.php b/app/Domains/Automation/Services/LogerAutomationService.php index 97f3f145..2d8ceee8 100644 --- a/app/Domains/Automation/Services/LogerAutomationService.php +++ b/app/Domains/Automation/Services/LogerAutomationService.php @@ -2,12 +2,12 @@ namespace App\Domains\Automation\Services; -use App\Domains\Automation\Models\Automation; -use App\Domains\Automation\Models\AutomationTask; use App\Domains\Integration\Actions\BHD; +use App\Domains\Automation\Models\Automation; use App\Domains\Integration\Actions\BHDAlert; -use App\Domains\Integration\Actions\OccurrenceCheckAutomation; +use App\Domains\Automation\Models\AutomationTask; use App\Domains\Integration\Actions\TransactionCreateEntry; +use App\Domains\Integration\Actions\OccurrenceAutomation; class LogerAutomationService { @@ -276,18 +276,18 @@ public static function services() ], 'type' => 'internal', ], - 'occurrenceChecks' => [ + 'Occurrences' => [ 'name' => 'occurrence_check', 'label' => 'Occurrence Check', 'logo' => '/images/meal-planner.png', - 'entity' => OccurrenceCheckAutomation::class, + 'entity' => OccurrenceAutomation::class, 'description' => 'Occurrence check', - 'fields' => json_encode(OccurrenceCheckAutomation::fieldConfig()), + 'fields' => json_encode(OccurrenceAutomation::fieldConfig()), 'actions' => [ [ - 'name' => 'createOccurrenceCheck', + 'name' => 'createOccurrence', 'label' => 'Create Occurrence Check', - 'entity' => OccurrenceCheckAutomation::class, + 'entity' => OccurrenceAutomation::class, 'description' => 'Create an occurrence check', 'config' => json_encode([ 'name' => [ diff --git a/app/Domains/Housing/Actions/RegisterOccurrence.php b/app/Domains/Housing/Actions/RegisterOccurrence.php index 1e7b1e7c..1fd09726 100644 --- a/app/Domains/Housing/Actions/RegisterOccurrence.php +++ b/app/Domains/Housing/Actions/RegisterOccurrence.php @@ -2,38 +2,41 @@ namespace App\Domains\Housing\Actions; +use Exception; +use App\Models\User; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Log; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; +use App\Domains\Housing\Data\OccurrenceData; use App\Domains\Transaction\Actions\SearchTransactions; class RegisterOccurrence { + private function softAdd(Occurrence $occurrence, $date) { + $lastDuration = $occurrence->last_date ? $this->getDaysDifference($occurrence->last_date, $date) : 0; + $log = (array) $occurrence->log ?? []; + $log[] = $date; + $occurrenceCount = count($log); + $totalDays = $occurrenceCount > 1 ? $this->getDaysDifference($log[0], $date) : $lastDuration; + + $occurrence->last_date = $date; + $occurrence->previous_days_count = $lastDuration; + $occurrence->occurrence_count = $occurrenceCount; + $occurrence->log = $log; + $occurrence->total_days = $totalDays; + $occurrence->avg_days_passed = $totalDays / $occurrenceCount; + } + public function add(int $teamId, string $name, string $date) { - $occurrence = OccurrenceCheck::where([ - 'team_id' => $teamId, - 'name' => $name, - ])->first(); - + $occurrence = Occurrence::byTeam($teamId)->byName($name)->first(); if ($occurrence && $occurrence->last_date !== $date) { - $lastDuration = $occurrence->last_date ? $this->getDaysDifference($occurrence->last_date, $date) : 0; - $totalDays = $occurrence->total_days + $lastDuration; - $occurrenceCount = $occurrence->occurrence_count + 1; - $avg = $totalDays / $occurrenceCount; - $log = (array) $occurrence->log ?? []; - $log[] = $date; + $this->softAdd($occurrence, $date); + $occurrence->save(); + return; + } - $occurrence->update([ - 'last_date' => $date, - 'previous_days_count' => $lastDuration, - 'total_days' => $totalDays, - 'avg_days_passed' => $avg, - 'occurrence_count' => $occurrenceCount, - 'log' => $log, - ]); - } elseif (! $occurrence) { - OccurrenceCheck::create([ + Occurrence::create([ 'name' => $name, 'team_id' => $teamId, 'last_date' => $date, @@ -41,14 +44,12 @@ public function add(int $teamId, string $name, string $date) 'total_days' => 0, 'avg_days_passed' => 0, 'log' => [], - ]); - - } + ]); } public function remove(int $id, string $date = null) { - $occurrence = OccurrenceCheck::find($id); + $occurrence = Occurrence::find($id); if ($occurrence && $occurrence->last_date) { $log = $occurrence->log; @@ -72,38 +73,37 @@ public function remove(int $id, string $date = null) } } - public function load(OccurrenceCheck $occurrence) + public function load(Occurrence $occurrence) { $transactions = (new SearchTransactions())->handle($occurrence->conditions); foreach ($transactions as $transaction) { try { - (new RegisterOccurrence())->add( - $transaction->team_id, - $occurrence->name, - $transaction->date - ); - } catch (\Exception $e) { - Log::error($e->getMessage()); - + $this->softAdd($occurrence, $transaction->date); + } catch (Exception) { continue; } } + $occurrence->update(); } - public function sync(OccurrenceCheck $occurrence) + public function sync(Occurrence $occurrence) { + $transactions = (new SearchTransactions())->handle($occurrence->conditions); + $occurrence->log = []; + $occurrence->saveQuietly(); - foreach ($transactions as $transaction) { + $dates = $transactions->pluck('date')->toArray(); + + foreach ($dates as $date) { try { - (new RegisterOccurrence())->add( - $transaction->team_id, - $occurrence->name, - $transaction->date + $this->softAdd( + $occurrence, + $date ); + $occurrence->save(); } catch (\Exception $e) { Log::error($e->getMessage()); - continue; } } @@ -113,4 +113,13 @@ private function getDaysDifference($startDate, $endDate, $format = 'Y-m-d') { return Carbon::createFromFormat($format, $endDate)->diffInDays(Carbon::createFromFormat($format, $startDate)); } + + public function fromImport(User $user, OccurrenceData $data) { + Occurrence::create([ + 'team_id' => $user->current_team_id, + 'user_id' => $user->id, + ...$data->toArray(), + "name" => $data->name . " copy" + ]); + } } diff --git a/app/Domains/Housing/Data/OccurrenceData.php b/app/Domains/Housing/Data/OccurrenceData.php new file mode 100644 index 00000000..63fd61e6 --- /dev/null +++ b/app/Domains/Housing/Data/OccurrenceData.php @@ -0,0 +1,38 @@ +occurrences; + return collect($this->occurrences)->map(function ($check) { + // if (count($check['log']) > 1) { + // dd($check["name"], $check['log']); + // } + + return [ + $check['name'], + $check['last_date'], + $check['previous_days_count'], + $check['avg_days_passed'], + $check['notify_on_avg'], + $check['notify_on_last_count'], + $check['conditions'], + $check['type'], + $check['log'], + $check['is_active'], + ]; + })->toArray(); + } + + public function headings(): array + { + return [ + 'Name', + 'Last occurrence at', + 'Previous duration(days)', + 'Frequency (avg in days)', + 'Notify on avg?', + 'Notify on previous duration?', + 'Conditions', + 'Type', + 'Logs', + 'Is active', + ]; } } diff --git a/app/Domains/Housing/Http/Controllers/OccurrenceController.php b/app/Domains/Housing/Http/Controllers/OccurrenceController.php index 613b7714..23776751 100644 --- a/app/Domains/Housing/Http/Controllers/OccurrenceController.php +++ b/app/Domains/Housing/Http/Controllers/OccurrenceController.php @@ -2,19 +2,20 @@ namespace App\Domains\Housing\Http\Controllers; -use App\Domains\Housing\Actions\RegisterOccurrence; -use App\Domains\Housing\Exports\OccurrenceExport; -use App\Domains\Housing\Models\OccurrenceCheck; -use App\Domains\Transaction\Actions\SearchTransactions; use App\Jobs\RunTeamChecks; -use Freesgen\Atmosphere\Http\InertiaController; use Illuminate\Http\Request; -use Illuminate\Support\Carbon as SupportCarbon; use Maatwebsite\Excel\Facades\Excel; +use App\Domains\Housing\Models\Occurrence; +use Freesgen\Atmosphere\Http\InertiaController; +use Illuminate\Support\Carbon as SupportCarbon; +use App\Domains\Housing\Exports\OccurrenceExport; +use App\Domains\Housing\Imports\OccurrenceImport; +use App\Domains\Housing\Actions\RegisterOccurrence; +use App\Domains\Transaction\Actions\SearchTransactions; class OccurrenceController extends InertiaController { - public function __construct(OccurrenceCheck $occurrence) + public function __construct(Occurrence $occurrence) { $this->model = $occurrence; $this->templates = [ @@ -30,11 +31,11 @@ public function __construct(OccurrenceCheck $occurrence) protected function getIndexProps(Request $request, $resources = null): array { return [ - 'linkedTypes' => OccurrenceCheck::getLinkedModels(), + 'linkedTypes' => Occurrence::getLinkedModels(), ]; } - public function addInstance(OccurrenceCheck $occurrence, RegisterOccurrence $registerOccurrence) + public function addInstance(Occurrence $occurrence, RegisterOccurrence $registerOccurrence) { $registerOccurrence->add( $occurrence->team_id, @@ -45,7 +46,7 @@ public function addInstance(OccurrenceCheck $occurrence, RegisterOccurrence $reg return redirect()->back(); } - public function removeLastInstance(OccurrenceCheck $occurrence, RegisterOccurrence $registerOccurrence) + public function removeLastInstance(Occurrence $occurrence, RegisterOccurrence $registerOccurrence) { $registerOccurrence->remove( $occurrence->id, @@ -54,17 +55,17 @@ public function removeLastInstance(OccurrenceCheck $occurrence, RegisterOccurren return redirect()->back(); } - public function automationPreview(OccurrenceCheck $occurrence, SearchTransactions $search) + public function automationPreview(Occurrence $occurrence, SearchTransactions $search) { return $search->handle($occurrence->conditions); } - public function automationLoad(OccurrenceCheck $occurrence, RegisterOccurrence $registerer) + public function automationLoad(Occurrence $occurrence, RegisterOccurrence $registerer) { return $registerer->load($occurrence); } - public function sync(OccurrenceCheck $occurrence, RegisterOccurrence $registerer) + public function sync(Occurrence $occurrence, RegisterOccurrence $registerer) { return $registerer->sync($occurrence); } @@ -76,8 +77,16 @@ public function syncAll() public function export() { - $dataToExport = new OccurrenceExport(OccurrenceCheck::where('team_id', request()->user()->current_team_id)->get()->toArray()); + $dataToExport = new OccurrenceExport(Occurrence::where('team_id', request()->user()->current_team_id)->get()->toArray()); return Excel::download($dataToExport, 'occurrences.xlsx'); } + + public function import(Request $request) + { + Excel::import(new OccurrenceImport($request->user()), $request->file('file')); + + return redirect()->back(); + } + } diff --git a/app/Domains/Housing/Http/Controllers/ProjectController.php b/app/Domains/Housing/Http/Controllers/ProjectController.php index a3436487..d5d29df8 100644 --- a/app/Domains/Housing/Http/Controllers/ProjectController.php +++ b/app/Domains/Housing/Http/Controllers/ProjectController.php @@ -2,7 +2,7 @@ namespace App\Domains\Housing\Http\Controllers; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use App\Http\Controllers\Controller; class ProjectController extends Controller @@ -10,7 +10,7 @@ class ProjectController extends Controller public function __invoke() { return inertia('Housing/Index', [ - 'checks' => OccurrenceCheck::where('team_id', auth()->user()->current_team_id)->limit(4)->get(), + 'checks' => Occurrence::where('team_id', auth()->user()->current_team_id)->limit(4)->get(), ]); } } diff --git a/app/Domains/Housing/Imports/OccurrenceImport.php b/app/Domains/Housing/Imports/OccurrenceImport.php new file mode 100644 index 00000000..b087d12c --- /dev/null +++ b/app/Domains/Housing/Imports/OccurrenceImport.php @@ -0,0 +1,29 @@ +fromImport($this->user, OccurrenceData::fromImport($row)); + } + + public function map($row): array + { + return $row; + } +} diff --git a/app/Domains/Housing/Models/OccurrenceCheck.php b/app/Domains/Housing/Models/Occurrence.php similarity index 82% rename from app/Domains/Housing/Models/OccurrenceCheck.php rename to app/Domains/Housing/Models/Occurrence.php index 0f5b5433..fd1f826e 100644 --- a/app/Domains/Housing/Models/OccurrenceCheck.php +++ b/app/Domains/Housing/Models/Occurrence.php @@ -2,16 +2,18 @@ namespace App\Domains\Housing\Models; -use App\Domains\Housing\Contracts\OccurrenceNotifyTypes; -use App\Domains\Transaction\Models\Transaction; use App\Events\OccurrenceCreated; -use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use App\Domains\Transaction\Models\Transaction; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use App\Domains\Housing\Contracts\OccurrenceNotifyTypes; -class OccurrenceCheck extends Model +class Occurrence extends Model { use HasFactory; + protected $table ="occurrence_checks"; + protected $fillable = [ 'team_id', 'user_id', @@ -71,8 +73,20 @@ public static function getForNotificationType(OccurrenceNotifyTypes $type) $activatedField = self::NOTIFY_FIELDS[$type->value]['activatedField']; $countField = self::NOTIFY_FIELDS[$type->value]['countField']; - return OccurrenceCheck::where($activatedField, true) + return Occurrence::where($activatedField, true) ->whereRaw("DATEDIFF( date_format(now(), '%Y-%m-%d'), last_date) > ($countField - $daysBefore)") ->get(); } + + public static function scopeByTeam($query, int $teamId) { + return $query->where([ + 'team_id' => $teamId, + ]); + } + + public static function scopeByName($query, string $name) { + $query->where([ + 'name' => $name, + ]); + } } diff --git a/app/Domains/Housing/routes.php b/app/Domains/Housing/routes.php index 5e3c9da3..a26131b9 100644 --- a/app/Domains/Housing/routes.php +++ b/app/Domains/Housing/routes.php @@ -1,28 +1,29 @@ group(function () { Route::get('/housing', ProjectController::class)->name('housing.overview'); Route::resource('/housing/occurrence', OccurrenceController::class); Route::controller(OccurrenceController::class)->group(function () { - Route::post('/housing/occurrence/{occurrence}/instances', 'addInstance'); - Route::delete('/housing/occurrence/{occurrence}/instances', 'removeLastInstance'); - Route::get('/housing/occurrence/{occurrence}/preview', 'automationPreview'); - Route::post('/housing/occurrence/{occurrence}/load', 'automationLoad'); - Route::post('/housing/occurrence/{occurrence}/sync', 'sync'); - Route::post('/housing/occurrence/sync-all', 'syncAll'); - Route::get('housing/occurrence-export', 'export')->name('occurrences.export'); + Route::post('/housing/occurrences/{occurrence}/instances', 'addInstance'); + Route::delete('/housing/occurrences/{occurrence}/instances', 'removeLastInstance'); + Route::get('/housing/occurrences/{occurrence}/preview', 'automationPreview'); + Route::post('/housing/occurrences/{occurrence}/load', 'automationLoad'); + Route::post('/housing/occurrences/{occurrence}/sync', 'sync'); + Route::post('/housing/occurrences/sync-all', 'syncAll'); + Route::get('housing/occurrence-export', 'export')->name('housing.occurrences.export'); + Route::post('housing/occurrence-import', 'import')->name('housing.occurrences.import'); }); Route::get('/housing/test', function () { - $occurrencesOnLast = OccurrenceCheck::getForNotificationType(OccurrenceNotifyTypes::LAST); - $occurrencesOnAvg = OccurrenceCheck::getForNotificationType(OccurrenceNotifyTypes::AVG); + $occurrencesOnLast = Occurrence::getForNotificationType(OccurrenceNotifyTypes::LAST); + $occurrencesOnAvg = Occurrence::getForNotificationType(OccurrenceNotifyTypes::AVG); return [ $occurrencesOnAvg, diff --git a/app/Domains/Integration/Actions/OccurrenceCheckAutomation.php b/app/Domains/Integration/Actions/OccurrenceCheckAutomation.php index 22c1f404..2176053f 100644 --- a/app/Domains/Integration/Actions/OccurrenceCheckAutomation.php +++ b/app/Domains/Integration/Actions/OccurrenceCheckAutomation.php @@ -3,8 +3,8 @@ namespace App\Domains\Integration\Actions; use App\Domains\Automation\Models\Automation; -use App\Domains\Automation\Models\AutomationTaskAction; use App\Domains\Housing\Actions\RegisterOccurrence; +use App\Domains\Automation\Models\AutomationTaskAction; class OccurrenceCheckAutomation { diff --git a/app/Domains/LogerProfile/Data/ProfileEntityType.php b/app/Domains/LogerProfile/Data/ProfileEntityType.php index bbea2017..4a854c5a 100644 --- a/app/Domains/LogerProfile/Data/ProfileEntityType.php +++ b/app/Domains/LogerProfile/Data/ProfileEntityType.php @@ -3,7 +3,7 @@ namespace App\Domains\LogerProfile\Data; use App\Domains\AppCore\Models\Category; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use App\Models\Account; use Exception; use Modules\Plan\Entities\Plan; @@ -11,7 +11,7 @@ enum ProfileEntityType: string { case Category = 'category'; - case OccurrenceCheck = 'occurrence_check'; + case Occurrence = 'occurrence_check'; case Plan = 'plan'; case Account = 'account'; case Menu = 'menu'; @@ -21,7 +21,7 @@ public function getClassName() { return match ($this->name) { self::Category->name => Category::class, - self::OccurrenceCheck->name => OccurrenceCheck::class, + self::Occurrence->name => Occurrence::class, self::Plan->name => Plan::class, self::Account->name => Account::class, self::Schedule->name => Planner::class, diff --git a/app/Events/OccurrenceCreated.php b/app/Events/OccurrenceCreated.php index 083e55c8..dba42da0 100644 --- a/app/Events/OccurrenceCreated.php +++ b/app/Events/OccurrenceCreated.php @@ -2,7 +2,7 @@ namespace App\Events; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Foundation\Events\Dispatchable; @@ -17,7 +17,7 @@ class OccurrenceCreated * * @return void */ - public function __construct(OccurrenceCheck $occurrence) + public function __construct(Occurrence $occurrence) { $this->occurrence = $occurrence; } diff --git a/app/Jobs/RunTeamChecks.php b/app/Jobs/RunTeamChecks.php index b6cb2ed9..56c9d032 100644 --- a/app/Jobs/RunTeamChecks.php +++ b/app/Jobs/RunTeamChecks.php @@ -3,7 +3,7 @@ namespace App\Jobs; use App\Domains\Housing\Actions\RegisterOccurrence; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -27,7 +27,7 @@ public function __construct(private string $teamId) */ public function handle(RegisterOccurrence $registerer): void { - $checks = OccurrenceCheck::where([ + $checks = Occurrence::where([ 'team_id' => $this->teamId, ])->get(); diff --git a/app/Listeners/TrashTeamSettings.php b/app/Listeners/TrashTeamSettings.php index e4334fbf..612c85c0 100644 --- a/app/Listeners/TrashTeamSettings.php +++ b/app/Listeners/TrashTeamSettings.php @@ -9,7 +9,7 @@ use App\Domains\Budget\Models\BudgetMonth; use App\Domains\Budget\Models\BudgetMovement; use App\Domains\Budget\Models\BudgetTarget; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use App\Domains\LogerProfile\Models\LogerProfile; use App\Domains\LogerProfile\Models\LogerProfileEntity; use App\Domains\Meal\Models\Ingredient; @@ -61,7 +61,7 @@ public function handle(TeamDeleted $event) BudgetTarget::where(['team_id' => $team->id])->delete(); // housing - OccurrenceCheck::where(['team_id' => $team->id])->delete(); + Occurrence::where(['team_id' => $team->id])->delete(); // Loger profile LogerProfile::where(['team_id' => $team->id])->delete(); diff --git a/app/Notifications/OccurrenceAlert.php b/app/Notifications/OccurrenceAlert.php index fe29818c..7df27c7b 100644 --- a/app/Notifications/OccurrenceAlert.php +++ b/app/Notifications/OccurrenceAlert.php @@ -2,7 +2,7 @@ namespace App\Notifications; -use App\Domains\Housing\Models\OccurrenceCheck; +use App\Domains\Housing\Models\Occurrence; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -11,14 +11,14 @@ class OccurrenceAlert extends Notification { use Queueable; - private OccurrenceCheck $occurrence; + private Occurrence $occurrence; /** * Create a new notification instance. * * @return void */ - public function __construct(OccurrenceCheck $occurrence) + public function __construct(Occurrence $occurrence) { $this->occurrence = $occurrence; } diff --git a/components.d.ts b/components.d.ts index 6eb68650..87ff7248 100644 --- a/components.d.ts +++ b/components.d.ts @@ -21,6 +21,10 @@ declare module 'vue' { IMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] IMdiClose: typeof import('~icons/mdi/close')['default'] IMdiEdit: typeof import('~icons/mdi/edit')['default'] + IMdiEllipsis: typeof import('~icons/mdi/ellipsis')['default'] + IMdiEllipsisV: typeof import('~icons/mdi/ellipsis-v')['default'] + IMdiEllipsisVertical: typeof import('~icons/mdi/ellipsis-vertical')['default'] + IMdiEllipsys: typeof import('~icons/mdi/ellipsys')['default'] IMdiExport: typeof import('~icons/mdi/export')['default'] IMdiFile: typeof import('~icons/mdi/file')['default'] IMdiFilter: typeof import('~icons/mdi/filter')['default'] diff --git a/resources/js/Components/AppUserMenu.vue b/resources/js/Components/AppUserMenu.vue index 2db42065..571a9907 100644 --- a/resources/js/Components/AppUserMenu.vue +++ b/resources/js/Components/AppUserMenu.vue @@ -24,9 +24,15 @@
Exports
+
+ Import +
Download CSV + + Import +
@@ -47,13 +53,14 @@ /> - diff --git a/resources/js/Components/ImportResourceModal.vue b/resources/js/Components/ImportResourceModal.vue index 3a996b63..01edd863 100644 --- a/resources/js/Components/ImportResourceModal.vue +++ b/resources/js/Components/ImportResourceModal.vue @@ -1,44 +1,4 @@ - - - + + + + diff --git a/resources/js/Components/LogerProfileModal.vue b/resources/js/Components/LogerProfileModal.vue index 8b16e9f1..ae60d5c9 100644 --- a/resources/js/Components/LogerProfileModal.vue +++ b/resources/js/Components/LogerProfileModal.vue @@ -80,7 +80,7 @@ const submit = () => { }; const update = () => { - const url = `/housing/occurrence/${props.formData.id}` + const url = `/housing/occurrences/${props.formData.id}` form.put(url, { onSuccess() { emitClose(); diff --git a/resources/js/Components/OccurrencePreview.vue b/resources/js/Components/OccurrencePreview.vue index e37789f3..b6682920 100644 --- a/resources/js/Components/OccurrencePreview.vue +++ b/resources/js/Components/OccurrencePreview.vue @@ -21,14 +21,14 @@ const isCalled = ref(false); onMounted(async () => { if (props.occurrenceId && !isCalled.value) { - const data = await fetch(`/housing/occurrence/${props.occurrenceId}/preview`) + const data = await fetch(`/housing/occurrences/${props.occurrenceId}/preview`) transactions.value = await data.json() isCalled.value = true } }) const load = () => { - axios.post(`/housing/occurrence/${props.occurrenceId}/load`).then(() => { + axios.post(`/housing/occurrences/${props.occurrenceId}/load`).then(() => { router.reload(); }) } diff --git a/resources/js/Components/organisms/ImportHolder.vue b/resources/js/Components/organisms/ImportHolder.vue index cd8baec3..e4d5b5ed 100644 --- a/resources/js/Components/organisms/ImportHolder.vue +++ b/resources/js/Components/organisms/ImportHolder.vue @@ -1,33 +1,18 @@ - - - + + + + diff --git a/resources/js/Pages/Finance/Category.vue b/resources/js/Pages/Finance/Category.vue index f476856d..50f4cd33 100644 --- a/resources/js/Pages/Finance/Category.vue +++ b/resources/js/Pages/Finance/Category.vue @@ -102,7 +102,9 @@ const transactionStatus = { controlsClass="bg-transparent text-body hover:bg-base-lvl-1" next-mode="month" /> - Import Transactions + + Import Transactions + diff --git a/resources/js/Pages/Housing/Equipment.vue b/resources/js/Pages/Housing/Equipment.vue index ca114c13..e99e6e26 100644 --- a/resources/js/Pages/Housing/Equipment.vue +++ b/resources/js/Pages/Housing/Equipment.vue @@ -85,16 +85,16 @@ const onSaved = () => { } const addInstance = (id) => { - router.post(`/housing/occurrence/${id}/instances`) + router.post(`/housing/occurrences/${id}/instances`) } const removeLastInstance = (id) => { - router.delete(`/housing/occurrence/${id}/instances`) + router.delete(`/housing/occurrences/${id}/instances`) } const handleDelete = (resource) => { if (confirm(`Are you sure you want to delete this check ${resource.name}?`)) { - router.delete(`/housing/occurrence/${resource.id}`) + router.delete(`/housing/occurrences/${resource.id}`) } } diff --git a/resources/js/Pages/Housing/Occurrence.vue b/resources/js/Pages/Housing/Occurrence.vue index add065b1..c6c6153e 100644 --- a/resources/js/Pages/Housing/Occurrence.vue +++ b/resources/js/Pages/Housing/Occurrence.vue @@ -30,22 +30,22 @@ const onSaved = () => { } const addInstance = (id: number) => { - router.post(`/housing/occurrence/${id}/instances`) + router.post(`/housing/occurrences/${id}/instances`) } const removeLastInstance = (id: number) => { - router.delete(`/housing/occurrence/${id}/instances`) + router.delete(`/housing/occurrences/${id}/instances`) } const handleDelete = (resource: OccurrenceItem) => { if (confirm(`Are you sure you want to delete this check ${resource.name}?`)) { - router.delete(`/housing/occurrence/${resource.id}`) + router.delete(`/housing/occurrences/${resource.id}`) } } const syncForm = useForm({}) const syncAll = () => { - syncForm.post(`/housing/occurrence/sync-all`) + syncForm.post(`/housing/occurrences/sync-all`) } const handleEdit = (resource: OccurrenceItem) => { @@ -63,7 +63,7 @@ const defaultOptions = { name: 'sync', label: 'Sync', handle(resource: OccurrenceItem) { - router.post(`/housing/occurrence/${resource.id}/sync`) + router.post(`/housing/occurrences/${resource.id}/sync`) } }, removed: { @@ -89,7 +89,7 @@ const handleOptions = (optionName: IOptionNames , transaction: ITransaction) =>