Compare commits
10 Commits
5bdb0a4209
...
d2d52b8f1b
Author | SHA1 | Date | |
---|---|---|---|
d2d52b8f1b | |||
9be2de5421 | |||
95abaa78a3 | |||
d9ca6ec6ba | |||
1e32502759 | |||
a4f2c3e87f | |||
3dc4b98959 | |||
b6592d7945 | |||
bc63a4074b | |||
2d5688b66e |
@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
**2025-04-10**
|
||||
**2025-04-13**
|
||||
- Fixed #132 - Inches / cm toggle for order page (and show unit on pdf)
|
||||
- Fixed #148 - Change amount label to unit price on order form (also textarea)
|
||||
- Fixed #147 - Resource lock sidebar settings
|
||||
- Fixed #134 - Fix Dashboard 'last 30 days'
|
||||
- Added #129 - Order product count sum
|
||||
- Fixed #145 - Default order sort is incorrect (newest will always show on top now)
|
||||
- Fixed #142 - Packing Slip content field should be required
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
use App\Models\ServiceFile;
|
||||
use App\Models\ServiceType;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Field;
|
||||
use Filament\Forms\Components\Grid;
|
||||
use Filament\Forms\Components\Group;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
@ -40,6 +41,22 @@
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
function recalculateRowTotal(callable $set, callable $get): void
|
||||
{
|
||||
$amounts = collect([
|
||||
$get('xs'),
|
||||
$get('s'),
|
||||
$get('m'),
|
||||
$get('l'),
|
||||
$get('xl'),
|
||||
$get('2xl'),
|
||||
$get('3xl'),
|
||||
$get('osfa'),
|
||||
])->map(fn ($val) => floatval($val ?? 0));
|
||||
|
||||
$set('total', $amounts->sum());
|
||||
}
|
||||
|
||||
class OrderResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Order::class;
|
||||
@ -52,6 +69,8 @@ public static function form(Form $form): Form
|
||||
{
|
||||
return $form->schema([
|
||||
Group::make()
|
||||
->columns(6)
|
||||
->columnSpan(2)
|
||||
->schema([
|
||||
Section::make([
|
||||
Grid::make(1)
|
||||
@ -140,9 +159,7 @@ public static function form(Form $form): Form
|
||||
->columnSpan(1)
|
||||
->hidden(fn (?Order $record) => $record === null)
|
||||
->extraAttributes(['class' => 'h-full']),
|
||||
])
|
||||
->columns(6)
|
||||
->columnSpan(2),
|
||||
]),
|
||||
|
||||
TableRepeater::make('order_products')
|
||||
->label('Garments')
|
||||
@ -161,29 +178,40 @@ public static function form(Form $form): Form
|
||||
TextInput::make('xs')
|
||||
->placeholder('xs')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('s')
|
||||
->placeholder('s')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('m')
|
||||
->placeholder('m')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('l')
|
||||
->placeholder('l')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('xl')
|
||||
->placeholder('xl')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('2xl')
|
||||
->placeholder('2xl')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('3xl')
|
||||
->placeholder('3xl')
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('osfa')
|
||||
->placeholder('osfa')
|
||||
->rules('numeric'),
|
||||
])
|
||||
->label('Sizes'),
|
||||
])->label('Sizes'),
|
||||
|
||||
Field::make('total_display')
|
||||
->dehydrated(false)
|
||||
->label('Total')
|
||||
->view('forms.components.row-total'),
|
||||
]),
|
||||
|
||||
Repeater::make('services')
|
||||
@ -207,6 +235,7 @@ public static function form(Form $form): Form
|
||||
->placeholder('Full name here (example: \'Embroidery\'')
|
||||
->required(),
|
||||
])
|
||||
->createOptionAction(fn ($action) => $action->modalWidth('sm'))
|
||||
->createOptionUsing(function (array $data): int {
|
||||
return ServiceType::create($data)->getKey();
|
||||
}),
|
||||
@ -230,34 +259,44 @@ public static function form(Form $form): Form
|
||||
->prefix('h')
|
||||
->rules('numeric'),
|
||||
])
|
||||
->label('Dimensions (inches)')
|
||||
->label('Dimensions')
|
||||
->columnSpan(4),
|
||||
|
||||
TextInput::make('amount')
|
||||
->label('Quantity')
|
||||
->live()
|
||||
->prefix('#')
|
||||
->columnSpan(2)
|
||||
->rules('numeric'),
|
||||
|
||||
TextInput::make('amount_price')
|
||||
->label('Amount')
|
||||
->label('Unit price')
|
||||
->prefix('$')
|
||||
->columnSpan(2)
|
||||
->rules('numeric'),
|
||||
]),
|
||||
|
||||
Grid::make(9)
|
||||
Grid::make(19)
|
||||
->schema([
|
||||
TextInput::make('serviceFileCode')
|
||||
->label('Code')
|
||||
->datalist(ServiceFile::all()->unique('code')->pluck('code')->toArray())
|
||||
->columnSpan(1)
|
||||
->columnSpan(2)
|
||||
->placeholder('A1234'),
|
||||
|
||||
Select::make('serviceFileSizeUnit')
|
||||
->columnSpan(2)
|
||||
->selectablePlaceholder(false)
|
||||
->label('Unit')
|
||||
->options([
|
||||
'in' => 'Inches',
|
||||
'cm' => 'cm',
|
||||
]),
|
||||
|
||||
Textarea::make('notes')
|
||||
->placeholder('Thread colors...')
|
||||
->columnSpan(8),
|
||||
->placeholder('Thread colors... (press enter for new line)')
|
||||
->autosize()
|
||||
->rows(1)
|
||||
->columnSpan(15),
|
||||
]),
|
||||
])
|
||||
->reorderable()
|
||||
|
@ -63,6 +63,7 @@ protected function handleRecordCreation(array $data): Order
|
||||
'width' => $service['serviceFileWidth'] ?? null,
|
||||
'height' => $service['serviceFileHeight'] ?? null,
|
||||
'setup_number' => $service['serviceFileSetupNumber'] ?? null,
|
||||
'size_unit' => $service['serviceFileSizeUnit'] ?? 'inch',
|
||||
]);
|
||||
|
||||
ProductService::create([
|
||||
|
@ -68,6 +68,7 @@ protected function mutateFormDataBeforeFill(array $data): array
|
||||
'serviceFileHeight' => $service->serviceFile->height ?? '',
|
||||
'serviceFileCode' => $service->serviceFile->code ?? '',
|
||||
'serviceFileSetupNumber' => $service->serviceFile->setup_number ?? '',
|
||||
'serviceFileSizeUnit' => $service->serviceFile->size_unit ?? 'inch',
|
||||
];
|
||||
}
|
||||
|
||||
@ -139,6 +140,7 @@ public function handleRecordUpdate(Model $record, array $data): Model
|
||||
'width' => $service['serviceFileWidth'] ?? null,
|
||||
'height' => $service['serviceFileHeight'] ?? null,
|
||||
'setup_number' => $service['serviceFileSetupNumber'] ?? null,
|
||||
'size_unit' => $service['serviceFileSizeUnit'] ?? 'inch',
|
||||
]);
|
||||
|
||||
ProductService::create([
|
||||
|
@ -10,7 +10,6 @@
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Components\Tab;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
|
||||
class ListOrders extends ListRecords
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ private function getOrdersPast30Days(): string
|
||||
->where('order_status', '!=', OrderStatus::READY_FOR_INVOICE)
|
||||
->where('order_status', '!=', OrderStatus::SHIPPED)
|
||||
->where('order_status', '!=', OrderStatus::INVOICED)
|
||||
->whereBetween('created_at', [now()->startOfMonth(), now()->endOfMonth()])
|
||||
->whereBetween('created_at', [now()->subDays(30), now()])
|
||||
->count();
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ class InvoiceController extends Controller
|
||||
public function pdf(int $id)
|
||||
{
|
||||
$invoice = Invoice::find($id);
|
||||
$url = strtolower('invoice-'.$invoice->internal_id.'.pdf');
|
||||
$url = strtolower($invoice->internal_id.'_'.$invoice->customer->company_name.'.pdf');
|
||||
|
||||
Pdf::view('pdf.invoice', ['invoice' => $invoice])
|
||||
->withBrowsershot(function (Browsershot $browsershot) {
|
||||
|
@ -19,6 +19,7 @@ class ServiceFile extends Model
|
||||
'width',
|
||||
'height',
|
||||
'setup_number',
|
||||
'size_unit',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@
|
||||
| or any other location as required by the application or its packages.
|
||||
*/
|
||||
|
||||
'version' => '20250410',
|
||||
'version' => '20250413',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
130
config/resource-lock.php
Normal file
130
config/resource-lock.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Models
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The models configuration specifies the classes that represent your application's
|
||||
| data objects. This configuration is used by the framework to interact with
|
||||
| the application's data models. You can even implement your own ResourceLock model.
|
||||
|
|
||||
*/
|
||||
|
||||
'models' => [
|
||||
'User' => \App\Models\User::class,
|
||||
// 'ResourceLock' => null,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filament Resource
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The resource lock filament resource displays all the current locks in place.
|
||||
| You are able to replace the resource Lock with your own resource class.
|
||||
|
|
||||
*/
|
||||
'resource' => [
|
||||
'class' => \Kenepa\ResourceLock\Resources\LockResource::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resource Unlocker Button
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The unlocker configuration specifies whether limited access is enabled for
|
||||
| the resource unlock button. If limited access is enabled, only specific
|
||||
| users or roles will be able to unlock locked resources directly from
|
||||
| the modal.
|
||||
|
|
||||
*/
|
||||
|
||||
'unlocker' => [
|
||||
'limited_access' => false,
|
||||
// 'gate' => ''
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Lock Notice
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The lock notice contains several configuration options for the modal
|
||||
| that is display when a resource is locked.
|
||||
|
|
||||
*/
|
||||
|
||||
'lock_notice' => [
|
||||
'display_resource_lock_owner' => false,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resource Lock Manager
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The resource lock manager provides a simple way to view all resource locks
|
||||
| of your application. It provides several ways to quickly unlock all or
|
||||
| specific resources within your app.
|
||||
|
|
||||
*/
|
||||
|
||||
'manager' => [
|
||||
'navigation_badge' => false,
|
||||
'navigation_icon' => 'heroicon-o-lock-closed',
|
||||
'navigation_label' => 'Resource Locks',
|
||||
'plural_label' => 'Resource Locks',
|
||||
'navigation_group' => 'Settings',
|
||||
'navigation_sort' => 200,
|
||||
'limited_access' => false,
|
||||
'should_register_navigation' => true,
|
||||
// 'gate' => ''
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Lock timeout (in minutes)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The lock_timeout configuration specifies the time interval, in minutes,
|
||||
| after which a lock on a resource will expire if it has not been manually
|
||||
| unlocked or released by the user.
|
||||
|
|
||||
*/
|
||||
|
||||
'lock_timeout' => 10,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Check Locks before saving
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The check_locks_before_saving configuration specifies whether a lock of a resource will be checked
|
||||
| before saving a resource if a tech-savvy user is able to bypass the locked
|
||||
| resource modal and attempt to save the resource. In some cases you may want to turns this off.
|
||||
| It's recommended to keep this on.
|
||||
|
|
||||
*/
|
||||
|
||||
'check_locks_before_saving' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Actions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Action classes are simple classes that execute some logic within the package.
|
||||
| If you want to add your own custom logic you are able to extend your own
|
||||
| class with class your overwriting.
|
||||
| Learn more about action classes: https://freek.dev/2442-strategies-for-making-laravel-packages-customizable
|
||||
|
|
||||
*/
|
||||
|
||||
'actions' => [
|
||||
'get_resource_lock_owner_action' => \Kenepa\ResourceLock\Actions\GetResourceLockOwnerAction::class,
|
||||
],
|
||||
];
|
@ -19,7 +19,6 @@ public function definition(): array
|
||||
'name' => $this->faker->word(),
|
||||
'width' => round($this->faker->randomFloat(2, 0, 10), 1),
|
||||
'height' => round($this->faker->randomFloat(2, 0, 10), 1),
|
||||
'unit' => 'inch',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ public function up(): void
|
||||
$table->foreignId('service_file_id')->nullable();
|
||||
$table->foreignId('service_type_id')->nullable();
|
||||
|
||||
// $table->string('service_type')->nullable();
|
||||
$table->string('placement')->nullable();
|
||||
$table->string('amount')->nullable();
|
||||
$table->string('amount_price')->nullable();
|
||||
|
@ -16,7 +16,6 @@ public function up(): void
|
||||
$table->decimal('height')->nullable();
|
||||
$table->string('unit')->default('inch');
|
||||
$table->integer('setup_number')->nullable();
|
||||
// $table->string('notes')->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('service_files', function (Blueprint $table) {
|
||||
$table->string('size_unit')->default('in');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('service_files', function (Blueprint $table) {
|
||||
$table->dropColumn('size_unit');
|
||||
});
|
||||
}
|
||||
};
|
@ -217,7 +217,7 @@ class="absolute inset-0 rotate-180 transition"
|
||||
|
||||
<div
|
||||
x-show="! isCollapsed"
|
||||
class="fi-fo-repeater-item-content px-4 py-2"
|
||||
class="fi-fo-repeater-item-content px-4 py-3"
|
||||
>
|
||||
{{ $item }}
|
||||
</div>
|
||||
|
43
resources/views/forms/components/row-total.blade.php
Normal file
43
resources/views/forms/components/row-total.blade.php
Normal file
@ -0,0 +1,43 @@
|
||||
@php
|
||||
$statePath = $getStatePath(); // e.g., data.order_products.UUID.total_display
|
||||
$rowPath = \Illuminate\Support\Str::beforeLast($statePath, '.total_display');
|
||||
|
||||
$xsPath = "{$rowPath}.xs";
|
||||
$sPath = "{$rowPath}.s";
|
||||
$mPath = "{$rowPath}.m";
|
||||
$lPath = "{$rowPath}.l";
|
||||
$xlPath = "{$rowPath}.xl";
|
||||
$xxlPath = "{$rowPath}.2xl";
|
||||
$xxxlPath = "{$rowPath}.3xl";
|
||||
$osfaPath = "{$rowPath}.osfa";
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
xs: @entangle($xsPath),
|
||||
s: @entangle($sPath),
|
||||
m: @entangle($mPath),
|
||||
l: @entangle($lPath),
|
||||
xl: @entangle($xlPath),
|
||||
_2xl: @entangle($xxlPath),
|
||||
_3xl: @entangle($xxxlPath),
|
||||
osfa: @entangle($osfaPath),
|
||||
}"
|
||||
>
|
||||
<!-- Filament TextInput to show the computed total -->
|
||||
<input
|
||||
type="text"
|
||||
x-bind:value="(
|
||||
(parseFloat(xs || 0)) +
|
||||
(parseFloat(s || 0)) +
|
||||
(parseFloat(m || 0)) +
|
||||
(parseFloat(l || 0)) +
|
||||
(parseFloat(xl || 0)) +
|
||||
(parseFloat(_2xl || 0)) +
|
||||
(parseFloat(_3xl || 0)) +
|
||||
(parseFloat(osfa || 0))
|
||||
).toFixed(0)"
|
||||
class="fi-input block w-full border-none py-1.5 text-base text-gray-950 transition duration-75 placeholder:text-gray-400 focus:ring-0 disabled:text-gray-500 disabled:[-webkit-text-fill-color:theme(colors.gray.500)] disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.400)] dark:text-white dark:placeholder:text-gray-500 dark:disabled:text-gray-400 dark:disabled:[-webkit-text-fill-color:theme(colors.gray.400)] dark:disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.500)] sm:text-sm sm:leading-6 bg-white/0 ps-3 pe-3"
|
||||
readonly
|
||||
/>
|
||||
</div>
|
@ -155,9 +155,9 @@
|
||||
<tbody>
|
||||
@foreach($order->orderProducts as $product)
|
||||
<tr>
|
||||
<td><code>{{$product->sku}}</code></td>
|
||||
<td class="text-uppercase">{{$product->product_name}}</td>
|
||||
<td class="text-uppercase">{{$product->color}}</td>
|
||||
<td><code style="font-size: 0.9rem;">{{$product->sku}}</code></td>
|
||||
<td class="text-uppercase">{{$product->product_name ?? ''}}</td>
|
||||
<td class="text-uppercase">{{$product->color ?? ''}}</td>
|
||||
<td style="width: 40px;">{{$product->productSizes()->get()->where('size', 'xs')->first()->amount ?? ''}}</td>
|
||||
<td style="width: 40px;">{{$product->productSizes()->get()->where('size', 's')->first()->amount ?? ''}}</td>
|
||||
<td style="width: 40px;">{{$product->productSizes()->get()->where('size', 'm')->first()->amount ?? ''}}</td>
|
||||
@ -176,7 +176,7 @@
|
||||
</div>
|
||||
<div class="row mt-3 text-end" style="font-size: 12px">
|
||||
<div class="col">
|
||||
TOTAL QUANTITY: {{$order->totalProductQuantity}}
|
||||
TOTAL QUANTITY: {{$order->totalProductQuantity ?? ''}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -190,6 +190,7 @@
|
||||
<th class="w-50">Logo Name & Instructions</th>
|
||||
<th>Width</th>
|
||||
<th>Height</th>
|
||||
<th>Unit</th>
|
||||
<th>QTY</th>
|
||||
</thead>
|
||||
|
||||
@ -198,32 +199,35 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="text-uppercase fw-bold">
|
||||
{{$service->placement}}
|
||||
{{$service->placement ?? ''}}
|
||||
</div>
|
||||
<br>
|
||||
<div class="text-uppercase">
|
||||
<code style="font-size: 14px">
|
||||
{{$service->serviceFile->code}}
|
||||
{{$service->serviceFile->code ?? ''}}
|
||||
</code>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-uppercase">
|
||||
{{$service->serviceFile->name}}
|
||||
{{$service->serviceFile->name ?? ''}}
|
||||
</div>
|
||||
<br>
|
||||
<div class="text-uppercase" style="color: #d63384;">
|
||||
{{$service->notes}}
|
||||
{{$service->notes ?? ''}}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{$service->serviceFile->width}}
|
||||
</td>
|
||||
<td>
|
||||
{{$service->serviceFile->height}}
|
||||
{{$service->serviceFile->height ?? ''}}
|
||||
</td>
|
||||
<td>
|
||||
{{$service->amount}}
|
||||
{{$service->serviceFile->size_unit ?? ''}}
|
||||
</td>
|
||||
<td>
|
||||
{{$service->amount ?? ''}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user