Update each repeater field 2 fields outside the repeater

I'm playing around with creating a invoice in Filament and I've created a form that has multiple fields outside a repeater and then a repeater that has multiple items inside it. After a change is made in any of the repeater fields, I need to update that that specific repeater item. But then I also want to update a few fields outside the repeater. Currently what I have is not working and I'm not quite sure why it's not. I'm sure it's in regards to the repeater items.
public static function form(Form $form): Form
{
Repeater::make('items')
->schema([
TextInput::make('title'),
TextInput::make('qty')
->live()
->afterStateUpdate(function (Get $get, Set $set) {
self::updateLineTotal($get, $set);
}),
TextInput::make('rate')
->live()
->afterStateUpdate(function (Get $get, Set $set) {
self::updateLineTotal($get, $set);
}),
TextInput::make('discount')
->live()
->afterStateUpdate(function (Get $get, Set $set) {
self::updateLineTotal($get, $set);
}),
TextInput::make('line_total')
->live(),
]),
TextInput::make('subtotal')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
TextInput::make('discount')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
TextInput::make('tax')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
TextInput::make('total')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
}
public static function form(Form $form): Form
{
Repeater::make('items')
->schema([
TextInput::make('title'),
TextInput::make('qty')
->live()
->afterStateUpdate(function (Get $get, Set $set) {
self::updateLineTotal($get, $set);
}),
TextInput::make('rate')
->live()
->afterStateUpdate(function (Get $get, Set $set) {
self::updateLineTotal($get, $set);
}),
TextInput::make('discount')
->live()
->afterStateUpdate(function (Get $get, Set $set) {
self::updateLineTotal($get, $set);
}),
TextInput::make('line_total')
->live(),
]),
TextInput::make('subtotal')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
TextInput::make('discount')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
TextInput::make('tax')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
TextInput::make('total')
->live()
->afterUpdateHydrated(function (Get $get, Set $set) {
self::updateTotals($get, $set);
})
->readOnly(),
}
1 Reply
ThatWebGuy
ThatWebGuy2mo ago
public static function updateLineTotal(Get $get, Set $set): void {
$lineTotal = $get('items.qty') * $get('items.rate');
if($get('items.discount_type') === 'fixed') {
$lineTotal -= $get('items.discount');
} else {
$lineTotal -= ($get('items.discount') / 100 * $lineTotal);
}
$set('line_total', number_format($lineTotal, 2, '.', ''));
}

public static function updateTotals(Get $get, Set $set): void {
$lineItems = collect($get('items'))->filter(fn($item) => !empty($item['qty']) && !empty($item['title']));

$subtotal = $lineItems->reduce(function($subtotal, $item) {
$linetotal = ($item['qty'] * $item['rate']);
if($item['discount_type'] === 'fixed') {
$linetotal -= $item['discount'];
} else {
$linetotal -= ($item['discount'] / 100 * $subtotal);
}
return $subtotal + $linetotal;
}, 0);

$set('sub_total', number_format($subtotal, 2, '.', ''));
$set('total', number_format($subtotal - ($subtotal - ($get('discount'))) + ($subtotal * ($get('tax') / 100)), 2, '.', ''));

}
public static function updateLineTotal(Get $get, Set $set): void {
$lineTotal = $get('items.qty') * $get('items.rate');
if($get('items.discount_type') === 'fixed') {
$lineTotal -= $get('items.discount');
} else {
$lineTotal -= ($get('items.discount') / 100 * $lineTotal);
}
$set('line_total', number_format($lineTotal, 2, '.', ''));
}

public static function updateTotals(Get $get, Set $set): void {
$lineItems = collect($get('items'))->filter(fn($item) => !empty($item['qty']) && !empty($item['title']));

$subtotal = $lineItems->reduce(function($subtotal, $item) {
$linetotal = ($item['qty'] * $item['rate']);
if($item['discount_type'] === 'fixed') {
$linetotal -= $item['discount'];
} else {
$linetotal -= ($item['discount'] / 100 * $subtotal);
}
return $subtotal + $linetotal;
}, 0);

$set('sub_total', number_format($subtotal, 2, '.', ''));
$set('total', number_format($subtotal - ($subtotal - ($get('discount'))) + ($subtotal * ($get('tax') / 100)), 2, '.', ''));

}