activity is now added when contract balance is changed
This commit is contained in:
parent
d54fc9914d
commit
342d9d0700
|
|
@ -223,7 +223,11 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr
|
|||
return back()->with('warning', __('contracts.edit_not_allowed_archived'));
|
||||
}
|
||||
|
||||
\DB::transaction(function () use ($request, $contract) {
|
||||
$balanceChanged = false;
|
||||
$oldBalance = null;
|
||||
$newBalance = null;
|
||||
|
||||
\DB::transaction(function () use ($request, $contract, &$balanceChanged, &$oldBalance, &$newBalance) {
|
||||
$contract->update([
|
||||
'reference' => $request->input('reference'),
|
||||
'type_id' => $request->input('type_id'),
|
||||
|
|
@ -254,6 +258,7 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr
|
|||
$accountData['type_id'] = $request->input('account_type_id');
|
||||
}
|
||||
if ($currentAccount) {
|
||||
$oldBalance = (float) $currentAccount->balance_amount;
|
||||
$currentAccount->update($accountData);
|
||||
if (array_key_exists('balance_amount', $accountData)) {
|
||||
$currentAccount->forceFill(['balance_amount' => $accountData['balance_amount']])->save();
|
||||
|
|
@ -264,6 +269,10 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr
|
|||
->update(['balance_amount' => $accountData['balance_amount'], 'updated_at' => now()]);
|
||||
$freshBal = (float) optional($currentAccount->fresh())->balance_amount;
|
||||
}
|
||||
$newBalance = $freshBal;
|
||||
if ($oldBalance !== $freshBal) {
|
||||
$balanceChanged = true;
|
||||
}
|
||||
} else {
|
||||
$freshBal = (float) optional($currentAccount->fresh())->balance_amount;
|
||||
}
|
||||
|
|
@ -276,6 +285,27 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr
|
|||
|
||||
});
|
||||
|
||||
// Fire activity if balance changed and settings require it
|
||||
if ($balanceChanged) {
|
||||
$contractSetting = \App\Models\ContractSetting::query()->first();
|
||||
if ($contractSetting && $contractSetting->create_activity_on_balance_change) {
|
||||
$note = str_replace(
|
||||
['{old_balance}', '{new_balance}', '{currency}'],
|
||||
[number_format($oldBalance, 2, '.', ''), number_format($newBalance, 2, '.', ''), 'EUR'],
|
||||
$contractSetting->activity_note_template ?? ''
|
||||
);
|
||||
\App\Models\Activity::query()->create([
|
||||
'due_date' => null,
|
||||
'amount' => $newBalance,
|
||||
'note' => $note,
|
||||
'action_id' => $contractSetting->default_action_id,
|
||||
'decision_id' => $contractSetting->default_decision_id,
|
||||
'client_case_id' => $contract->client_case_id,
|
||||
'contract_id' => $contract->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve segment filter if present
|
||||
$segment = request('segment');
|
||||
|
||||
|
|
|
|||
56
app/Http/Controllers/ContractSettingController.php
Normal file
56
app/Http/Controllers/ContractSettingController.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
class ContractSettingController extends Controller
|
||||
{
|
||||
public function edit(): \Inertia\Response
|
||||
{
|
||||
$setting = \App\Models\ContractSetting::query()->first();
|
||||
if (! $setting) {
|
||||
$setting = \App\Models\ContractSetting::query()->create([
|
||||
'create_activity_on_balance_change' => false,
|
||||
'default_action_id' => null,
|
||||
'default_decision_id' => null,
|
||||
'activity_note_template' => 'Sprememba stanja pogodbe: {old_balance} → {new_balance} {currency}',
|
||||
]);
|
||||
}
|
||||
|
||||
$decisions = \App\Models\Decision::query()->orderBy('name')->get(['id', 'name']);
|
||||
$actions = \App\Models\Action::query()
|
||||
->with(['decisions:id'])
|
||||
->orderBy('name')
|
||||
->get()
|
||||
->map(function (\App\Models\Action $a) {
|
||||
return [
|
||||
'id' => $a->id,
|
||||
'name' => $a->name,
|
||||
'decision_ids' => $a->decisions->pluck('id')->values(),
|
||||
];
|
||||
});
|
||||
|
||||
return \Inertia\Inertia::render('Settings/Contracts/Index', [
|
||||
'setting' => [
|
||||
'id' => $setting->id,
|
||||
'create_activity_on_balance_change' => (bool) $setting->create_activity_on_balance_change,
|
||||
'default_action_id' => $setting->default_action_id,
|
||||
'default_decision_id' => $setting->default_decision_id,
|
||||
'activity_note_template' => $setting->activity_note_template,
|
||||
],
|
||||
'decisions' => $decisions,
|
||||
'actions' => $actions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(\App\Http\Requests\UpdateContractSettingRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
$setting = \App\Models\ContractSetting::query()->firstOrFail();
|
||||
|
||||
$data['create_activity_on_balance_change'] = (bool) ($data['create_activity_on_balance_change'] ?? false);
|
||||
|
||||
$setting->update($data);
|
||||
|
||||
return back()->with('success', 'Nastavitve shranjene.');
|
||||
}
|
||||
}
|
||||
23
app/Http/Requests/UpdateContractSettingRequest.php
Normal file
23
app/Http/Requests/UpdateContractSettingRequest.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateContractSettingRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'create_activity_on_balance_change' => ['sometimes', 'boolean'],
|
||||
'default_action_id' => ['nullable', 'integer', 'exists:actions,id'],
|
||||
'default_decision_id' => ['nullable', 'integer', 'exists:decisions,id'],
|
||||
'activity_note_template' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
}
|
||||
15
app/Models/ContractSetting.php
Normal file
15
app/Models/ContractSetting.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ContractSetting extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'create_activity_on_balance_change',
|
||||
'default_action_id',
|
||||
'default_decision_id',
|
||||
'activity_note_template',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('contract_settings', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->boolean('create_activity_on_balance_change')->default(false);
|
||||
$table->foreignId('default_action_id')->nullable()->constrained('actions')->nullOnDelete();
|
||||
$table->foreignId('default_decision_id')->nullable()->constrained('decisions')->nullOnDelete();
|
||||
$table->string('activity_note_template', 255)->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('contract_settings');
|
||||
}
|
||||
};
|
||||
159
resources/js/Pages/Settings/Contracts/Index.vue
Normal file
159
resources/js/Pages/Settings/Contracts/Index.vue
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<script setup>
|
||||
import AppLayout from "@/Layouts/AppLayout.vue";
|
||||
import { useForm } from "@inertiajs/vue3";
|
||||
import { computed, watch } from "vue";
|
||||
import AppCard from "@/Components/app/ui/card/AppCard.vue";
|
||||
import CardTitle from "@/Components/ui/card/CardTitle.vue";
|
||||
import { FileText } from "lucide-vue-next";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
import InputLabel from "@/Components/InputLabel.vue";
|
||||
import { Checkbox } from "@/Components/ui/checkbox";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/Components/ui/select";
|
||||
|
||||
const props = defineProps({
|
||||
setting: Object,
|
||||
decisions: Array,
|
||||
actions: Array,
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
create_activity_on_balance_change: !!props.setting?.create_activity_on_balance_change,
|
||||
default_action_id: props.setting?.default_action_id ?? null,
|
||||
default_decision_id: props.setting?.default_decision_id ?? null,
|
||||
activity_note_template:
|
||||
props.setting?.activity_note_template ??
|
||||
"Sprememba stanja pogodbe: {old_balance} → {new_balance} {currency}",
|
||||
});
|
||||
|
||||
const filteredDecisions = computed(() => {
|
||||
const actionId = form.default_action_id;
|
||||
if (!actionId) return [];
|
||||
const action = props.actions?.find((a) => a.id === actionId);
|
||||
if (!action || !action.decision_ids) return [];
|
||||
const ids = new Set(action.decision_ids);
|
||||
return (props.decisions || []).filter((d) => ids.has(d.id));
|
||||
});
|
||||
|
||||
watch(
|
||||
() => form.default_action_id,
|
||||
(newVal) => {
|
||||
if (!newVal) {
|
||||
form.default_decision_id = null;
|
||||
} else {
|
||||
const ids = new Set((filteredDecisions.value || []).map((d) => d.id));
|
||||
if (!ids.has(form.default_decision_id)) {
|
||||
form.default_decision_id = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const submit = () => {
|
||||
form.put(route("settings.contract.update"), {
|
||||
preserveScroll: true,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppLayout title="Nastavitve pogodb">
|
||||
<template #header></template>
|
||||
<div class="max-w-3xl mx-auto p-6">
|
||||
<AppCard
|
||||
title=""
|
||||
padding="none"
|
||||
class="p-0! gap-0"
|
||||
header-class="py-3! px-4 gap-0 text-muted-foreground"
|
||||
body-class=""
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<FileText :size="18" />
|
||||
<CardTitle class="uppercase">Nastavitve pogodb</CardTitle>
|
||||
</div>
|
||||
</template>
|
||||
<div class="space-y-6 p-4 border-t">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="create-activity"
|
||||
v-model="form.create_activity_on_balance_change"
|
||||
/>
|
||||
<InputLabel for="create-activity" class="text-sm font-normal cursor-pointer">
|
||||
Ustvari aktivnost ob spremembi odprtega zneska pogodbe
|
||||
</InputLabel>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<InputLabel for="default-action">Privzeto dejanje</InputLabel>
|
||||
<Select v-model="form.default_action_id">
|
||||
<SelectTrigger id="default-action">
|
||||
<SelectValue placeholder="— Brez —" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="null">— Brez —</SelectItem>
|
||||
<SelectItem v-for="a in actions" :key="a.id" :value="a.id">{{
|
||||
a.name
|
||||
}}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div v-if="form.errors.default_action_id" class="text-sm text-red-600 mt-1">
|
||||
{{ form.errors.default_action_id }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputLabel for="default-decision">Privzeta odločitev</InputLabel>
|
||||
<Select
|
||||
v-model="form.default_decision_id"
|
||||
:disabled="!form.default_action_id"
|
||||
>
|
||||
<SelectTrigger id="default-decision">
|
||||
<SelectValue placeholder="— Najprej izberite dejanje —" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="null">— Najprej izberite dejanje —</SelectItem>
|
||||
<SelectItem v-for="d in filteredDecisions" :key="d.id" :value="d.id">{{
|
||||
d.name
|
||||
}}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div
|
||||
v-if="form.errors.default_decision_id"
|
||||
class="text-sm text-red-600 mt-1"
|
||||
>
|
||||
{{ form.errors.default_decision_id }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<InputLabel for="note-template">Predloga opombe aktivnosti</InputLabel>
|
||||
<Input id="note-template" v-model="form.activity_note_template" />
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
Podprti žetoni: {old_balance}, {new_balance}, {currency}
|
||||
</p>
|
||||
<div
|
||||
v-if="form.errors.activity_note_template"
|
||||
class="text-sm text-red-600 mt-1"
|
||||
>
|
||||
{{ form.errors.activity_note_template }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button variant="outline" @click="form.reset()">Ponastavi</Button>
|
||||
<Button @click="submit" :disabled="form.processing">Shrani</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AppCard>
|
||||
</div>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
|
@ -52,6 +52,12 @@ const settingsCards = [
|
|||
route: "settings.contractConfigs.index",
|
||||
icon: FileText,
|
||||
},
|
||||
{
|
||||
title: "Contract Settings",
|
||||
description: "Auto-activity triggers on contract balance changes.",
|
||||
route: "settings.contract.edit",
|
||||
icon: FileText,
|
||||
},
|
||||
{
|
||||
title: "Archive Settings",
|
||||
description: "Define rules for archiving or soft-deleting aged data.",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
use App\Http\Controllers\ClientCaseContoller;
|
||||
use App\Http\Controllers\ClientController;
|
||||
use App\Http\Controllers\ContractConfigController;
|
||||
use App\Http\Controllers\ContractSettingController;
|
||||
use App\Http\Controllers\FieldJobController;
|
||||
use App\Http\Controllers\FieldJobSettingController;
|
||||
use App\Http\Controllers\ImportController;
|
||||
|
|
@ -518,6 +519,10 @@
|
|||
Route::get('settings/installment', [InstallmentSettingController::class, 'edit'])->name('settings.installment.edit');
|
||||
Route::put('settings/installment', [InstallmentSettingController::class, 'update'])->name('settings.installment.update');
|
||||
|
||||
// settings - contract settings
|
||||
Route::get('settings/contract', [ContractSettingController::class, 'edit'])->name('settings.contract.edit');
|
||||
Route::put('settings/contract', [ContractSettingController::class, 'update'])->name('settings.contract.update');
|
||||
|
||||
Route::get('types/address', function (Request $request) {
|
||||
$types = App\Models\Person\AddressType::all();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user