#help with repeaters and updating data pls

24 messages · Page 1 of 1 (latest)

fierce frost
#

Hello. I've got a Purchase Order resource with a repeater field on it. The repeater field is for items, and each item can have a name, quantity, and received quantity.

When creating a purchase order, a user can add as many items as they like and set a quantity. The received quantity defaults to 0.

The user can then upload invoices associated with the purchase order. In so doing, here's what I want to happen:

  1. The user should be able to click an "Update Inventory" button on the invoice record to open a dialog with all of the existing items on that purchase order.
  2. The items will be shown with their name and quantity disabled and unable to be edited, but the received quantity will be editable.
  3. The user can edit the received quantity ONLY to a max of the quantity. The quantity is the upper limit.
  4. On submit, it should increment or decrement the item's quantity in the repeater field and save the updated record.
  5. On submit, it should also find each of the items in the Products table and increment or decrement their quantity accordingly.

I've been losing my mind over this for the past hour or so because I can't figure out how to do it.

I've got this so far:

DB::transaction(function () use ($record, $data) {
    // Get the updated items from the modal
    $updatedItems = $data['items'];
    // Get the original items from the purchase order
    $originalItems = $record->purchaseOrder->items;

    // Initialize an array to hold the updated items for saving
    $updatedItemsArray = [];

    // Process the received quantities
    foreach ($updatedItems as $updatedItem) {
        // Find the matching original item in the purchase order
        $matchingItem = collect($originalItems)->firstWhere('name', $updatedItem['name']);
        if ($matchingItem) {
            // Calculate the difference in received quantity
            $previousReceivedQuantity = $matchingItem['received_quantity'];
            $newReceivedQuantity = $updatedItem['received_quantity'];
            $difference = $newReceivedQuantity - $previousReceivedQuantity;

            // Update the received quantity in the original item
            $matchingItem['received_quantity'] = $newReceivedQuantity;

            // Increment or decrement inventory based on the difference
            $product = Product::where('name', $updatedItem['name'])->first();
            if ($product) {
                if ($difference > 0) {
                    // If the received quantity increased, increment the inventory
                    $product->increment('inventory', $difference);
                } elseif ($difference < 0) {
                    // If the received quantity decreased, decrement the inventory
                    $product->decrement('inventory', abs($difference));
                }
            }

            // Add the updated item to the array
            $updatedItemsArray[] = $matchingItem;
        }
    }

    // Step 4: Save the updated items back to the purchase order
    $record->purchaseOrder->items = $updatedItemsArray;
    $record->purchaseOrder->save();
});

This works except I need to make the name and quantity fields disabled and dehydrated(false) because otherwise the user can edit them. However, when I do that, they're not in the $data array and I'm not able to match it.

rapid pond
#

So you want ->disabled() but ->dehydrated(true) I believe

fierce frost
rapid pond
#

I don't believe so, I am fairly sure it gets it from the injection state, but the saving state. i.e. on the backend level not from the input.

You can test it if it's a highly level of security concern.

fierce frost
rapid pond
fierce frost
rapid pond
#

maxValue(10)

fierce frost
#

Well, I don't know the quantity in the code.

#

I need to grab the max value from the quantity field in the repeater for that respective item.

rapid pond
#

where does the max value come from?

#

The item? / product quanity that's left?

#
->maxValue(fn($record) => $record->qty)

Like that?

fierce frost
# rapid pond ```php ->maxValue(fn($record) => $record->qty) ``` Like that?

No.

I've got a PurchaseOrder model. It has an items field on it that's a JSON field cast as an array, which in Filament is used for a repeater.

The repeater looks like this:

Repeater::make('items')->schema([
    TextInput::make('name')->required()->label('Name'),
    TextInput::make('quantity')->required()->numeric()->label('Quantity'),
    TextInput::make('received_quantity')->required()->numeric()->default(0)->label('Received Quantity'),
    MoneyInput::make('unit_cost')->required()->numeric()->label('Unit Cost'),
    TextInput::make('tax_rate')->required()->numeric()->label('Tax Rate')->suffix('%'),
])->required()->columns()->columnSpanFull()
    ->itemLabel(fn (array $state): ?string => $state['name'] ?? null),
#
[{"name":"Box, Medium","quantity":"10","unit_cost":"200","tax_rate":"5","received_quantity":"6"},{"name":"Packing Tape","quantity":"2","unit_cost":"500","tax_rate":"5","received_quantity":"0"}]

This is what that looks like in the database.

#

The received quantity for "Box, Medium" CANNOT be higher than its quantity, which is 10. So, if I put 11, it should error. For the packing tape, it cannot be 3, since the max quantity for that one is 2.

#
TextInput::make('received_quantity')->required()->numeric()->minValue(0)->maxValue(fn (
    VendorInvoice $record,
    Get $get
) => collect($record->purchaseOrder->items)->firstWhere('name',
    $get('name'))['quantity'])->label('Received Quantity')
#

@rapid pond I've got this and it works. However, using $get I find is a bit icky because someone can change the value of name in developer tools to be ANOTHER item's name and it'd update the max value allowed for received quantity, no?

rapid pond
fierce frost
#

The max value field is quantity, so it's disabled as well, yes.

rapid pond
#

So there any attempt to change a disabled field will result in getting the original value since disabled is dehydrated.

fierce frost
#

Gotcha. Thank you!

rapid pond
#

Its the same reason why models can be unguarded in filamentphp, because we validate them all securely and only allow data to be retained from the backend, the frontend is mearly input never truly a validator.