Allow budget update to have webhooks controlled with "fire_webhooks"

This commit is contained in:
James Cole
2025-09-14 08:59:00 +02:00
parent c2d3f5da16
commit 935453796e
4 changed files with 59 additions and 48 deletions

View File

@@ -57,30 +57,25 @@ class UpdateController extends Controller
); );
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/budgets/updateBudget
*
* Update a budget.
*/
public function update(UpdateRequest $request, Budget $budget): JsonResponse public function update(UpdateRequest $request, Budget $budget): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$budget = $this->repository->update($budget, $data); $data['fire_webhooks'] = $data['fire_webhooks'] ?? true;
$manager = $this->getManager(); $budget = $this->repository->update($budget, $data);
$manager = $this->getManager();
// enrich // enrich
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new BudgetEnrichment(); $enrichment = new BudgetEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$budget = $enrichment->enrichSingle($budget); $budget = $enrichment->enrichSingle($budget);
/** @var BudgetTransformer $transformer */ /** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class); $transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new Item($budget, $transformer, 'budgets'); $resource = new Item($budget, $transformer, 'budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }

View File

@@ -59,6 +59,9 @@ class UpdateRequest extends FormRequest
'auto_budget_type' => ['auto_budget_type', 'convertString'], 'auto_budget_type' => ['auto_budget_type', 'convertString'],
'auto_budget_amount' => ['auto_budget_amount', 'convertString'], 'auto_budget_amount' => ['auto_budget_amount', 'convertString'],
'auto_budget_period' => ['auto_budget_period', 'convertString'], 'auto_budget_period' => ['auto_budget_period', 'convertString'],
// webhooks
'fire_webhooks' => ['fire_webhooks','boolean']
]; ];
$allData = $this->getAllData($fields); $allData = $this->getAllData($fields);
if (array_key_exists('auto_budget_type', $allData)) { if (array_key_exists('auto_budget_type', $allData)) {
@@ -91,6 +94,9 @@ class UpdateRequest extends FormRequest
'auto_budget_currency_code' => 'exists:transaction_currencies,code', 'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()], 'auto_budget_amount' => ['nullable', new IsValidPositiveAmount()],
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
// webhooks
'fire_webhooks' => [new IsBoolean()],
]; ];
} }

View File

@@ -67,16 +67,22 @@ class BudgetObserver
public function updated(Budget $budget): void public function updated(Budget $budget): void
{ {
Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name)); Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
$user = $budget->user;
/** @var MessageGeneratorInterface $engine */ // this is a lame trick to communicate with the observer.
$engine = app(MessageGeneratorInterface::class); $singleton = PreferencesSingleton::getInstance();
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget)); if (true === $singleton->getPreference('fire_webhooks_budget_update')) {
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET); $user = $budget->user;
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__)); /** @var MessageGeneratorInterface $engine */
event(new RequestedSendWebhookMessages()); $engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($budget));
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
$engine->generateMessages();
Log::debug(sprintf('send event RequestedSendWebhookMessages from %s', __METHOD__));
event(new RequestedSendWebhookMessages());
}
} }
public function deleting(Budget $budget): void public function deleting(Budget $budget): void

View File

@@ -86,7 +86,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
public function budgetedInPeriod(Carbon $start, Carbon $end): array public function budgetedInPeriod(Carbon $start, Carbon $end): array
{ {
app('log')->debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('Now in budgetedInPeriod("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d')));
$return = []; $return = [];
/** @var BudgetLimitRepository $limitRepository */ /** @var BudgetLimitRepository $limitRepository */
@@ -98,12 +98,12 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
/** @var Budget $budget */ /** @var Budget $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); Log::debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name));
$limits = $limitRepository->getBudgetLimits($budget, $start, $end); $limits = $limitRepository->getBudgetLimits($budget, $start, $end);
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
foreach ($limits as $limit) { foreach ($limits as $limit) {
app('log')->debug(sprintf('Budget limit #%d', $limit->id)); Log::debug(sprintf('Budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency; $currency = $limit->transactionCurrency;
$rate = $converter->getCurrencyRate($currency, $primaryCurrency, $end); $rate = $converter->getCurrencyRate($currency, $primaryCurrency, $end);
$currencyCode = $currency->code; $currencyCode = $currency->code;
@@ -125,7 +125,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) {
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], (string) $limit->amount); $return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], (string) $limit->amount);
$return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']); $return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); Log::debug(sprintf('Add full amount [1]: %s', $limit->amount));
continue; continue;
} }
@@ -133,7 +133,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) {
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], (string) $limit->amount); $return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], (string) $limit->amount);
$return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']); $return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); Log::debug(sprintf('Add full amount [2]: %s', $limit->amount));
continue; continue;
} }
@@ -142,7 +142,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
$amount = bcmul(bcdiv((string) $limit->amount, (string) $total), (string) $days); $amount = bcmul(bcdiv((string) $limit->amount, (string) $total), (string) $days);
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $amount); $return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $amount);
$return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']); $return[$currencyCode]['pc_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug( Log::debug(
sprintf( sprintf(
'Amount per day: %s (%s over %d days). Total amount for %d days: %s', 'Amount per day: %s (%s over %d days). Total amount for %d days: %s',
bcdiv((string) $limit->amount, (string) $total), bcdiv((string) $limit->amount, (string) $total),
@@ -203,19 +203,19 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array
{ {
app('log')->debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d')));
$return = []; $return = [];
/** @var BudgetLimitRepository $limitRepository */ /** @var BudgetLimitRepository $limitRepository */
$limitRepository = app(BudgetLimitRepository::class); $limitRepository = app(BudgetLimitRepository::class);
$limitRepository->setUser($this->user); $limitRepository->setUser($this->user);
app('log')->debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name)); Log::debug(sprintf('Budget #%d: "%s"', $budget->id, $budget->name));
$limits = $limitRepository->getBudgetLimits($budget, $start, $end); $limits = $limitRepository->getBudgetLimits($budget, $start, $end);
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
foreach ($limits as $limit) { foreach ($limits as $limit) {
app('log')->debug(sprintf('Budget limit #%d', $limit->id)); Log::debug(sprintf('Budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency; $currency = $limit->transactionCurrency;
$return[$currency->id] ??= [ $return[$currency->id] ??= [
'id' => (string) $currency->id, 'id' => (string) $currency->id,
@@ -228,14 +228,14 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
// same period // same period
if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) { if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)) {
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $limit->amount); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $limit->amount);
app('log')->debug(sprintf('Add full amount [1]: %s', $limit->amount)); Log::debug(sprintf('Add full amount [1]: %s', $limit->amount));
continue; continue;
} }
// limit is inside of date range // limit is inside of date range
if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) { if ($start->lte($limit->start_date) && $end->gte($limit->end_date)) {
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $limit->amount); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $limit->amount);
app('log')->debug(sprintf('Add full amount [2]: %s', $limit->amount)); Log::debug(sprintf('Add full amount [2]: %s', $limit->amount));
continue; continue;
} }
@@ -243,7 +243,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
$days = $this->daysInOverlap($limit, $start, $end); $days = $this->daysInOverlap($limit, $start, $end);
$amount = bcmul(bcdiv((string) $limit->amount, (string) $total), (string) $days); $amount = bcmul(bcdiv((string) $limit->amount, (string) $total), (string) $days);
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount); $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount);
app('log')->debug( Log::debug(
sprintf( sprintf(
'Amount per day: %s (%s over %d days). Total amount for %d days: %s', 'Amount per day: %s (%s over %d days). Total amount for %d days: %s',
bcdiv((string) $limit->amount, (string) $total), bcdiv((string) $limit->amount, (string) $total),
@@ -283,7 +283,11 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
*/ */
public function update(Budget $budget, array $data): Budget public function update(Budget $budget, array $data): Budget
{ {
app('log')->debug('Now in update()'); Log::debug('Now in update()');
// this is a lame trick to communicate with the observer.
$singleton = PreferencesSingleton::getInstance();
$singleton->setPreference('fire_webhooks_budget_update', $data['fire_webhooks'] ?? true);
$oldName = $budget->name; $oldName = $budget->name;
if (array_key_exists('name', $data)) { if (array_key_exists('name', $data)) {
@@ -331,13 +335,13 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
->where('rule_actions.action_value', $oldName) ->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*']) ->get(['rule_actions.*'])
; ;
app('log')->debug(sprintf('Found %d actions to update.', $actions->count())); Log::debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */ /** @var RuleAction $action */
foreach ($actions as $action) { foreach ($actions as $action) {
$action->action_value = $newName; $action->action_value = $newName;
$action->save(); $action->save();
app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
} }
} }
@@ -350,13 +354,13 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
->where('rule_triggers.trigger_value', $oldName) ->where('rule_triggers.trigger_value', $oldName)
->get(['rule_triggers.*']) ->get(['rule_triggers.*'])
; ;
app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count())); Log::debug(sprintf('Found %d triggers to update.', $triggers->count()));
/** @var RuleTrigger $trigger */ /** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) { foreach ($triggers as $trigger) {
$trigger->trigger_value = $newName; $trigger->trigger_value = $newName;
$trigger->save(); $trigger->save();
app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value));
} }
} }
@@ -487,17 +491,17 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
public function findBudget(?int $budgetId, ?string $budgetName): ?Budget public function findBudget(?int $budgetId, ?string $budgetName): ?Budget
{ {
app('log')->debug('Now in findBudget()'); Log::debug('Now in findBudget()');
app('log')->debug(sprintf('Searching for budget with ID #%d...', $budgetId)); Log::debug(sprintf('Searching for budget with ID #%d...', $budgetId));
$result = $this->find((int) $budgetId); $result = $this->find((int) $budgetId);
if (!$result instanceof Budget && null !== $budgetName && '' !== $budgetName) { if (!$result instanceof Budget && null !== $budgetName && '' !== $budgetName) {
app('log')->debug(sprintf('Searching for budget with name %s...', $budgetName)); Log::debug(sprintf('Searching for budget with name %s...', $budgetName));
$result = $this->findByName($budgetName); $result = $this->findByName($budgetName);
} }
if ($result instanceof Budget) { if ($result instanceof Budget) {
app('log')->debug(sprintf('Found budget #%d: %s', $result->id, $result->name)); Log::debug(sprintf('Found budget #%d: %s', $result->id, $result->name));
} }
app('log')->debug(sprintf('Found result is null? %s', var_export(!$result instanceof Budget, true))); Log::debug(sprintf('Found result is null? %s', var_export(!$result instanceof Budget, true)));
return $result; return $result;
} }
@@ -594,7 +598,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
public function spentInPeriod(Carbon $start, Carbon $end): array public function spentInPeriod(Carbon $start, Carbon $end): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
@@ -656,7 +660,7 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
public function spentInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array public function spentInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
@@ -740,8 +744,8 @@ class BudgetRepository implements BudgetRepositoryInterface, UserGroupInterface
] ]
); );
} catch (QueryException $e) { } catch (QueryException $e) {
app('log')->error($e->getMessage()); Log::error($e->getMessage());
app('log')->error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('400002: Could not store budget.', 0, $e); throw new FireflyException('400002: Could not store budget.', 0, $e);
} }