changes
This commit is contained in:
parent
ee1af56d03
commit
f40c3d0f2e
|
|
@ -172,34 +172,76 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr
|
|||
]);
|
||||
|
||||
$initial = $request->input('initial_amount');
|
||||
$balance = $request->input('balance_amount');
|
||||
$shouldUpsertAccount = (! is_null($initial)) || (! is_null($balance)) || $request->has('account_type_id');
|
||||
// Use has() to distinguish between an omitted field and an explicit 0 / null intent
|
||||
$balanceFieldPresent = $request->has('balance_amount');
|
||||
$balance = $balanceFieldPresent ? $request->input('balance_amount') : null;
|
||||
// Always allow updating existing account even if only balance set to 0 (or unchanged) so user can correct it.
|
||||
$hasType = $request->has('account_type_id');
|
||||
$shouldUpsertAccount = ($contract->account()->exists()) || (! is_null($initial)) || $balanceFieldPresent || $hasType;
|
||||
if ($shouldUpsertAccount) {
|
||||
$accountData = [];
|
||||
// Track old balance before applying changes
|
||||
$oldBalance = (float) optional($contract->account)->balance_amount;
|
||||
$currentAccount = $contract->account; // newest (latestOfMany)
|
||||
$oldBalance = (float) optional($currentAccount)->balance_amount;
|
||||
if (! is_null($initial)) {
|
||||
$accountData['initial_amount'] = $initial;
|
||||
}
|
||||
if (! is_null($balance)) {
|
||||
$accountData['balance_amount'] = $balance;
|
||||
// If the balance field was present in the request payload we always apply it (allow setting to 0)
|
||||
if ($balanceFieldPresent) {
|
||||
// Allow explicitly setting to 0, fallback to 0 if null provided
|
||||
$accountData['balance_amount'] = $balance ?? 0;
|
||||
}
|
||||
if ($request->has('account_type_id')) {
|
||||
$accountData['type_id'] = $request->input('account_type_id');
|
||||
}
|
||||
|
||||
if ($contract->account) {
|
||||
$contract->account->update($accountData);
|
||||
if ($currentAccount) {
|
||||
$currentAccount->update($accountData);
|
||||
if (array_key_exists('balance_amount', $accountData)) {
|
||||
$currentAccount->forceFill(['balance_amount' => $accountData['balance_amount']])->save();
|
||||
$freshBal = (float) optional($currentAccount->fresh())->balance_amount;
|
||||
if ((float) $freshBal !== (float) $accountData['balance_amount']) {
|
||||
\DB::table('accounts')
|
||||
->where('id', $currentAccount->id)
|
||||
->update(['balance_amount' => $accountData['balance_amount'], 'updated_at' => now()]);
|
||||
$freshBal = (float) optional($currentAccount->fresh())->balance_amount;
|
||||
}
|
||||
} else {
|
||||
$freshBal = (float) optional($currentAccount->fresh())->balance_amount;
|
||||
}
|
||||
} else {
|
||||
// For create, ensure defaults exist if not provided
|
||||
$accountData = array_merge(['initial_amount' => 0, 'balance_amount' => 0], $accountData);
|
||||
$contract->account()->create($accountData);
|
||||
$created = $contract->account()->create($accountData);
|
||||
$freshBal = (float) optional($created->fresh())->balance_amount;
|
||||
}
|
||||
// If multiple historical accounts exist, log them and optionally propagate update to all to keep consistent
|
||||
$allAccounts = \DB::table('accounts')->where('contract_id', $contract->id)->orderBy('id')->get(['id','balance_amount','initial_amount']);
|
||||
if ($allAccounts->count() > 1 && array_key_exists('balance_amount', $accountData)) {
|
||||
// Propagate balance to all for consistency (comment out if not desired)
|
||||
\DB::table('accounts')->where('contract_id', $contract->id)->update(['balance_amount' => $accountData['balance_amount'], 'updated_at' => now()]);
|
||||
$freshBal = (float) \DB::table('accounts')->where('contract_id', $contract->id)->latest('id')->value('balance_amount');
|
||||
}
|
||||
try {
|
||||
$accountCount = $allAccounts->count();
|
||||
logger()->info('Contract account upsert', [
|
||||
'contract_id' => $contract->id,
|
||||
'request_initial' => $initial,
|
||||
'request_balance_present' => $balanceFieldPresent,
|
||||
'request_balance' => $balance,
|
||||
'request_account_type_id' => $request->input('account_type_id'),
|
||||
'account_data_applied' => $accountData,
|
||||
'old_balance' => $oldBalance,
|
||||
'new_balance_after_update' => $freshBal,
|
||||
'accounts_for_contract' => $accountCount,
|
||||
'accounts_snapshot' => $allAccounts,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
// ignore logging errors
|
||||
}
|
||||
|
||||
// After update/create, if balance_amount changed (and not through a payment), log an activity with before/after
|
||||
if (array_key_exists('balance_amount', $accountData)) {
|
||||
// Guard against null account (e.g., if creation failed silently earlier)
|
||||
$newAccount = $contract->account; // single relationship access
|
||||
$newAccount = $contract->account; // refreshed latest
|
||||
$newBalance = $newAccount ? (float) optional($newAccount->fresh())->balance_amount : $oldBalance;
|
||||
if ($newAccount && $newBalance !== $oldBalance) {
|
||||
try {
|
||||
|
|
@ -231,6 +273,24 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr
|
|||
return to_route('clientCase.show', ['client_case' => $clientCase, 'segment' => $segment]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug endpoint: list all account rows for a contract (only in debug mode).
|
||||
*/
|
||||
public function debugContractAccounts(ClientCase $clientCase, string $uuid, Request $request)
|
||||
{
|
||||
abort_unless(config('app.debug'), 404);
|
||||
$contract = $clientCase->contracts()->where('uuid', $uuid)->firstOrFail(['id','uuid','reference']);
|
||||
$accounts = \DB::table('accounts')
|
||||
->where('contract_id', $contract->id)
|
||||
->orderBy('id')
|
||||
->get(['id','contract_id','initial_amount','balance_amount','type_id','created_at','updated_at']);
|
||||
return response()->json([
|
||||
'contract' => $contract,
|
||||
'accounts' => $accounts,
|
||||
'count' => $accounts->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeActivity(ClientCase $clientCase, Request $request)
|
||||
{
|
||||
try {
|
||||
|
|
@ -1048,8 +1108,8 @@ public function show(ClientCase $clientCase)
|
|||
// Prepare contracts and a reference map.
|
||||
// Only apply active/inactive filtering IF a segment filter is provided.
|
||||
$contractsQuery = $case->contracts()
|
||||
// Only select lean columns to avoid oversize JSON / headers
|
||||
->select(['id', 'uuid', 'reference', 'start_date', 'end_date', 'active', 'type_id', 'client_case_id', 'created_at'])
|
||||
// Only select lean columns to avoid oversize JSON / headers (include description for UI display)
|
||||
->select(['id', 'uuid', 'reference', 'start_date', 'end_date', 'description', 'active', 'type_id', 'client_case_id', 'created_at'])
|
||||
->with([
|
||||
'type:id,name',
|
||||
// Use closure for account to avoid ambiguous column names with latestOfMany join
|
||||
|
|
@ -1060,9 +1120,14 @@ public function show(ClientCase $clientCase)
|
|||
'accounts.type_id',
|
||||
'accounts.initial_amount',
|
||||
'accounts.balance_amount',
|
||||
]);
|
||||
'accounts.promise_date',
|
||||
'accounts.created_at',
|
||||
'accounts.updated_at', // include updated_at so FE can detect changes & for debugging
|
||||
])->orderByDesc('accounts.id');
|
||||
},
|
||||
'segments:id,name',
|
||||
// Eager load objects so newly created objects appear without full reload logic issues
|
||||
'objects:id,contract_id,reference,name,description,type,created_at',
|
||||
]);
|
||||
|
||||
$contractsQuery->orderByDesc('created_at');
|
||||
|
|
@ -1089,6 +1154,25 @@ public function show(ClientCase $clientCase)
|
|||
// pathological memory / header growth. Frontend can request more via future endpoint.
|
||||
$contracts = $contractsQuery->limit(500)->get();
|
||||
|
||||
// TEMP DEBUG: log what balances are being sent to Inertia (remove once issue resolved)
|
||||
try {
|
||||
logger()->info('Show contracts balances', [
|
||||
'case_id' => $case->id,
|
||||
'contract_count' => $contracts->count(),
|
||||
'contracts' => $contracts->map(fn($c) => [
|
||||
'id' => $c->id,
|
||||
'uuid' => $c->uuid,
|
||||
'reference' => $c->reference,
|
||||
'account_id' => optional($c->account)->id,
|
||||
'initial_amount' => optional($c->account)->initial_amount,
|
||||
'balance_amount' => optional($c->account)->balance_amount,
|
||||
'account_updated_at' => optional($c->account)->updated_at,
|
||||
])->toArray(),
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
// swallow
|
||||
}
|
||||
|
||||
$contractRefMap = [];
|
||||
foreach ($contracts as $c) {
|
||||
$contractRefMap[$c->id] = $c->reference;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@ class Account extends Model
|
|||
'balance_amount',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'initial_amount' => 'decimal:4',
|
||||
'balance_amount' => 'decimal:4',
|
||||
'promise_date' => 'date',
|
||||
];
|
||||
}
|
||||
|
||||
public function debtor(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(\App\Models\Person\Person::class, 'debtor_id');
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ public function segments(): BelongsToMany
|
|||
|
||||
public function account(): HasOne
|
||||
{
|
||||
// Use latestOfMany to always surface newest account snapshot if multiple exist.
|
||||
return $this->hasOne(\App\Models\Account::class)
|
||||
->latestOfMany()
|
||||
->with('type');
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import SectionTitle from "@/Components/SectionTitle.vue";
|
|||
import TextInput from "@/Components/TextInput.vue";
|
||||
import CurrencyInput from "@/Components/CurrencyInput.vue";
|
||||
import DatePickerField from "@/Components/DatePickerField.vue";
|
||||
import { useForm } from "@inertiajs/vue3";
|
||||
import { useForm, router } from "@inertiajs/vue3";
|
||||
import { watch, nextTick, ref as vRef } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -95,6 +95,10 @@ watch(
|
|||
|
||||
const storeOrUpdate = () => {
|
||||
const isEdit = !!formContract.uuid;
|
||||
// Debug: log payload being sent to verify balance_amount presence
|
||||
try {
|
||||
console.debug('Submitting contract form', JSON.parse(JSON.stringify(formContract)));
|
||||
} catch (e) {}
|
||||
const options = {
|
||||
onBefore: () => {
|
||||
formContract.start_date = formContract.start_date;
|
||||
|
|
@ -103,6 +107,21 @@ const storeOrUpdate = () => {
|
|||
close();
|
||||
// keep state clean; reset to initial
|
||||
if (!isEdit) formContract.reset();
|
||||
// After edit ensure contracts list reflects updated balance
|
||||
if (isEdit) {
|
||||
try {
|
||||
const params = {};
|
||||
try {
|
||||
const url = new URL(window.location.href);
|
||||
const seg = url.searchParams.get('segment');
|
||||
if (seg) params.segment = seg;
|
||||
} catch (e) {}
|
||||
router.visit(route('clientCase.show', { client_case: props.client_case.uuid, ...params }), {
|
||||
preserveScroll: true,
|
||||
replace: true,
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
},
|
||||
preserveScroll: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ const props = defineProps({
|
|||
all_segments: { type: Array, default: () => [] },
|
||||
});
|
||||
|
||||
// Debug: log incoming contract balances (remove after fix)
|
||||
try {
|
||||
console.debug('Contracts received (balances):', props.contracts.map(c => ({ ref: c.reference, bal: c?.account?.balance_amount })));
|
||||
} catch (e) {}
|
||||
|
||||
const emit = defineEmits(["edit", "delete", "add-activity"]);
|
||||
|
||||
const formatDate = (d) => {
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@
|
|||
// client-case / contract
|
||||
Route::post('client-cases/{client_case:uuid}/contract', [ClientCaseContoller::class, 'storeContract'])->name('clientCase.contract.store');
|
||||
Route::put('client-cases/{client_case:uuid}/contract/{uuid}', [ClientCaseContoller::class, 'updateContract'])->name('clientCase.contract.update');
|
||||
Route::get('client-cases/{client_case:uuid}/contract/{uuid}/debug-accounts', [ClientCaseContoller::class, 'debugContractAccounts'])->name('clientCase.contract.debugAccounts');
|
||||
Route::delete('client-cases/{client_case:uuid}/contract/{uuid}', [ClientCaseContoller::class, 'deleteContract'])->name('clientCase.contract.delete');
|
||||
// client-case / contract / objects
|
||||
Route::post('client-cases/{client_case:uuid}/contract/{uuid}/objects', [CaseObjectController::class, 'store'])->name('clientCase.contract.object.store');
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user