From 3c2469481a6c27696a81444a5f56217ff6af95d9 Mon Sep 17 00:00:00 2001 From: Artha Date: Fri, 21 Feb 2025 15:43:23 +0800 Subject: [PATCH 1/3] feat: timesheets pdf --- ...ReturnItemInPalletReturnWithStoredItem.php | 1 + ...ndexStoredItemDeltasInProcessForPallet.php | 8 +- .../UI/ShowStoredItemAuditForPallet.php | 2 - .../Timesheet/Pdf/PdfTimesheet.php | 55 ++++++ .../Timesheet/UI/IndexTimesheets.php | 21 ++- ...edItemDeltasInProcessForPalletResource.php | 2 +- app/Models/Procurement/StockDelivery.php | 1 - app/Services/QueryBuilder.php | 3 +- ...12_add_scope_to_stock_deliveries_table.php | 3 +- resources/views/hr/timesheets.blade.php | 178 ++++++++++++++++++ routes/grp/web/org/hr.php | 3 +- 11 files changed, 262 insertions(+), 15 deletions(-) create mode 100644 app/Actions/HumanResources/Timesheet/Pdf/PdfTimesheet.php create mode 100644 resources/views/hr/timesheets.blade.php diff --git a/app/Actions/Fulfilment/PalletReturnItem/PickPalletReturnItemInPalletReturnWithStoredItem.php b/app/Actions/Fulfilment/PalletReturnItem/PickPalletReturnItemInPalletReturnWithStoredItem.php index 9ab7653362..c838cdfeca 100644 --- a/app/Actions/Fulfilment/PalletReturnItem/PickPalletReturnItemInPalletReturnWithStoredItem.php +++ b/app/Actions/Fulfilment/PalletReturnItem/PickPalletReturnItemInPalletReturnWithStoredItem.php @@ -36,6 +36,7 @@ public function handle(PalletReturnItem $palletReturnItem, array $modelData): Pa return DB::transaction(function () use ($palletReturnItem, $modelData) { $quantity = Arr::get($modelData, 'quantity_picked'); $palletStoredItemQuantity = $palletReturnItem->palletStoredItem->quantity; + $this->update($palletReturnItem, $modelData); StoreStoredItemMovementFromPicking::run($palletReturnItem, [ diff --git a/app/Actions/Fulfilment/StoredItemAudit/UI/IndexStoredItemDeltasInProcessForPallet.php b/app/Actions/Fulfilment/StoredItemAudit/UI/IndexStoredItemDeltasInProcessForPallet.php index 26bc5accbd..9b7e311958 100644 --- a/app/Actions/Fulfilment/StoredItemAudit/UI/IndexStoredItemDeltasInProcessForPallet.php +++ b/app/Actions/Fulfilment/StoredItemAudit/UI/IndexStoredItemDeltasInProcessForPallet.php @@ -12,8 +12,6 @@ use App\Actions\Fulfilment\WithFulfilmentCustomerSubNavigation; use App\Actions\OrgAction; use App\Actions\Traits\Authorisations\WithFulfilmentAuthorisation; -use App\Enums\Fulfilment\Pallet\PalletStateEnum; -use App\Enums\Fulfilment\Pallet\PalletStatusEnum; use App\InertiaTable\InertiaTable; use App\Models\Fulfilment\FulfilmentCustomer; use App\Models\Fulfilment\Pallet; @@ -47,10 +45,10 @@ public function handle(StoredItemAudit $storedItemAudit, $prefix = null): Length } $query = QueryBuilder::for(StoredItem::class); - $query->join('pallet_stored_items', 'pallet_stored_items.stored_item_id', '=', 'stored_items.id' ) + $query->join('pallet_stored_items', 'pallet_stored_items.stored_item_id', '=', 'stored_items.id') ->where('pallet_stored_items.pallet_id', $storedItemAudit->scope->id); - - $query->leftJoin('stored_item_audit_deltas', function ($join) use ($storedItemAudit) { + + $query->leftJoin('stored_item_audit_deltas', function ($join) use ($storedItemAudit) { $join->on('pallet_stored_items.stored_item_id', '=', 'stored_item_audit_deltas.stored_item_id') ->where('stored_item_audit_deltas.stored_item_audit_id', '=', $storedItemAudit->id) ->where('stored_item_audit_deltas.pallet_id', '=', $storedItemAudit->scope->id); diff --git a/app/Actions/Fulfilment/StoredItemAudit/UI/ShowStoredItemAuditForPallet.php b/app/Actions/Fulfilment/StoredItemAudit/UI/ShowStoredItemAuditForPallet.php index cf0f7b7de8..26fdba2634 100644 --- a/app/Actions/Fulfilment/StoredItemAudit/UI/ShowStoredItemAuditForPallet.php +++ b/app/Actions/Fulfilment/StoredItemAudit/UI/ShowStoredItemAuditForPallet.php @@ -9,7 +9,6 @@ namespace App\Actions\Fulfilment\StoredItemAudit\UI; -use App\Actions\Fulfilment\FulfilmentCustomer\ShowFulfilmentCustomer; use App\Actions\Fulfilment\Pallet\UI\ShowPallet; use App\Actions\Fulfilment\StoredItemAuditDelta\UI\IndexStoredItemAuditDeltas; use App\Actions\Fulfilment\WithFulfilmentCustomerSubNavigation; @@ -20,7 +19,6 @@ use App\Http\Resources\Fulfilment\StoredItemAuditDeltasResource; use App\Http\Resources\Fulfilment\StoredItemAuditResource; use App\Http\Resources\Fulfilment\StoredItemDeltasInProcessForPalletResource; -use App\Http\Resources\Fulfilment\StoredItemDeltasInProcessResource; use App\Models\Fulfilment\Fulfilment; use App\Models\Fulfilment\FulfilmentCustomer; use App\Models\Fulfilment\Pallet; diff --git a/app/Actions/HumanResources/Timesheet/Pdf/PdfTimesheet.php b/app/Actions/HumanResources/Timesheet/Pdf/PdfTimesheet.php new file mode 100644 index 0000000000..672edf370a --- /dev/null +++ b/app/Actions/HumanResources/Timesheet/Pdf/PdfTimesheet.php @@ -0,0 +1,55 @@ + + * Created: Thu, 25 Jul 2024 14:54:12 Malaysia Time, Kuala Lumpur, Malaysia + * Copyright (c) 2024, Raul A Perusquia Flores + */ + +namespace App\Actions\HumanResources\Timesheet\Pdf; + +use App\Actions\Traits\WithExportData; +use App\Models\SysAdmin\Organisation; +use Lorisleiva\Actions\ActionRequest; +use Lorisleiva\Actions\Concerns\AsAction; +use Lorisleiva\Actions\Concerns\WithAttributes; +use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf as PDF; +use Symfony\Component\HttpFoundation\Response; + +class PdfTimesheet +{ + use AsAction; + use WithAttributes; + use WithExportData; + + /** + * @throws \Mpdf\MpdfException + */ + public function handle(Organisation $organisation) + { + $filename = __('Timesheets - ') . $organisation->name . '.pdf'; + $config = [ + 'title' => $filename, + 'margin_left' => 8, + 'margin_right' => 8, + 'margin_top' => 2, + 'margin_bottom' => 2, + 'auto_page_break' => true, + 'auto_page_break_margin' => 10 + ]; + + return PDF::chunkLoadView('', 'hr.timesheets', [ + 'filename' => $filename, + 'organisation' => $organisation, + 'employees' => $organisation->employees()->limit(10)->get(), + ], [], $config)->stream($filename); + } + + /** + * @throws \Mpdf\MpdfException + */ + public function asController(Organisation $organisation, ActionRequest $request): Response + { + return $this->handle($organisation); + } +} diff --git a/app/Actions/HumanResources/Timesheet/UI/IndexTimesheets.php b/app/Actions/HumanResources/Timesheet/UI/IndexTimesheets.php index 88653cf767..757d3b98bd 100644 --- a/app/Actions/HumanResources/Timesheet/UI/IndexTimesheets.php +++ b/app/Actions/HumanResources/Timesheet/UI/IndexTimesheets.php @@ -68,7 +68,7 @@ public function handle(Group|Organisation|Employee|Guest $parent, ?string $prefi $query->whereDate('timesheets.date', now()->format('Y-m-d')); } - $query->withFilterPeriod('created_at'); + $query->withFilterPeriod('date'); $query->select([ 'timesheets.id', 'timesheets.date', @@ -228,7 +228,24 @@ public function htmlResponse(LengthAwarePaginator $timesheets, ActionRequest $re 'afterTitle' => $afterTitle, 'iconRight' => $iconRight, 'subNavigation' => $subNavigation, - ], + 'actions' => [ + [ + 'type' => 'button', + 'style' => 'tertiary', + 'label' => 'PDF', + 'target' => '_blank', + 'icon' => 'fal fa-file-pdf', + 'key' => 'action', + 'route' => [ + 'name' => 'grp.org.hr.timesheets.export', + 'parameters' => [ + 'organisation' => $this->parent->slug, + ...$request->query, + ] + ] + ] + ] + ], 'tabs' => [ 'current' => $this->tab, diff --git a/app/Http/Resources/Fulfilment/StoredItemDeltasInProcessForPalletResource.php b/app/Http/Resources/Fulfilment/StoredItemDeltasInProcessForPalletResource.php index 2ebe179b0d..11790ab5e6 100644 --- a/app/Http/Resources/Fulfilment/StoredItemDeltasInProcessForPalletResource.php +++ b/app/Http/Resources/Fulfilment/StoredItemDeltasInProcessForPalletResource.php @@ -1,4 +1,5 @@ getModel()->getTable(); $periodType = array_key_first(request()->input(($prefix ? $prefix.'_' : '').'period') ?? []); if ($periodType) { $periodData = $this->validatePeriod($periodType, $prefix); if ($periodData) { - $this->whereBetween($column, [$periodData['start'], $periodData['end']]); + $this->whereBetween($table.'.'.$column, [$periodData['start'], $periodData['end']]); } } diff --git a/database/migrations/2025_02_21_054612_add_scope_to_stock_deliveries_table.php b/database/migrations/2025_02_21_054612_add_scope_to_stock_deliveries_table.php index 782cf90862..9c3acebaeb 100644 --- a/database/migrations/2025_02_21_054612_add_scope_to_stock_deliveries_table.php +++ b/database/migrations/2025_02_21_054612_add_scope_to_stock_deliveries_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. * diff --git a/resources/views/hr/timesheets.blade.php b/resources/views/hr/timesheets.blade.php new file mode 100644 index 0000000000..7cd41cd788 --- /dev/null +++ b/resources/views/hr/timesheets.blade.php @@ -0,0 +1,178 @@ + + + {{ $filename }} + + + + + +

+ + + + +
+ {{$organisation->name}} +
+ {{$organisation->name}} +
+
+ {{$organisation->address->address_line_1}} +
+
+ {{$organisation->address->address_line_2}} +
+
+ {{$organisation->address->locality}} {{$organisation->address->postal_code}} +
+
+ www.{{$organisation->email}} +
+
+
+ + + + +@foreach($employees as $employee) + + + + +
+
+
+ {{ __('Employee') }} + ({{ $employee->contact_name }}) +
+
+
+
+ + + + + + + + + + + + @foreach($employee->timesheets as $timesheet) + + + + + + + @endforeach + + +
{{ __('Date') }}{{ __('Start At') }}{{ __('End At') }}{{ __('Break Duration') }}
{{ $timesheet->date?->format('Y-m-d') }}{{ $timesheet->start_at?->format('Y-m-d H:i') }}{{ $timesheet->end_at?->format('Y-m-d H:i') }}{{ Carbon\CarbonInterval::seconds($timesheet->breaks_duration)->cascade()->forHumans() }}
+
+@endforeach + +
+ +
+
+ + diff --git a/routes/grp/web/org/hr.php b/routes/grp/web/org/hr.php index 34d27cdd37..502609864b 100644 --- a/routes/grp/web/org/hr.php +++ b/routes/grp/web/org/hr.php @@ -28,6 +28,7 @@ use App\Actions\HumanResources\Employee\UI\ShowEmployee; use App\Actions\HumanResources\JobPosition\UI\IndexJobPositions; use App\Actions\HumanResources\JobPosition\UI\ShowJobPosition; +use App\Actions\HumanResources\Timesheet\Pdf\PdfTimesheet; use App\Actions\HumanResources\Timesheet\UI\IndexTimesheets; use App\Actions\HumanResources\Timesheet\UI\ShowTimesheet; use App\Actions\HumanResources\Workplace\UI\CreateWorkplace; @@ -76,9 +77,9 @@ Route::get('/calendars/{calendar}', ShowCalendar::class)->name('calendars.show'); Route::get('/timesheets', IndexTimesheets::class)->name('timesheets.index'); +Route::get('/timesheets-export', PdfTimesheet::class)->name('timesheets.export'); Route::get('/timesheets/{timesheet}', ShowTimesheet::class)->name('timesheets.show'); - Route::get('/workplaces', IndexWorkplaces::class)->name('workplaces.index'); Route::get('/workplaces/create', CreateWorkplace::class)->name('workplaces.create'); Route::get('/workplaces/export', ExportWorkplaces::class)->name('workplaces.export'); From 639189048495505680f55285fac042e0b974b8bb Mon Sep 17 00:00:00 2001 From: Ganes556 Date: Fri, 21 Feb 2025 15:50:33 +0800 Subject: [PATCH 2/3] table date filter need filter time --- app/Actions/Traits/WithDashboard.php | 1 + app/Services/QueryBuilder.php | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/Actions/Traits/WithDashboard.php b/app/Actions/Traits/WithDashboard.php index 88415ee937..6ab2c4f6a0 100644 --- a/app/Actions/Traits/WithDashboard.php +++ b/app/Actions/Traits/WithDashboard.php @@ -137,6 +137,7 @@ protected function getIntervalPercentage($intervalData, string $prefix, $key, $t public function getDateIntervalFilter($interval): string { + // TODO: #1461 $intervals = [ '1y' => now()->subYear(), '1q' => now()->subQuarter(), diff --git a/app/Services/QueryBuilder.php b/app/Services/QueryBuilder.php index bbf89d592e..b62b9f36e6 100644 --- a/app/Services/QueryBuilder.php +++ b/app/Services/QueryBuilder.php @@ -184,10 +184,14 @@ public function withBetweenDates(array $allowedColumns, ?string $prefix = null): if (count($parts) === 2) { [$start, $end] = $parts; - - // Normalize the start and end dates - $start = trim($start) . ' 00:00:00'; - $end = trim($end) . ' 23:59:59'; + // TODO: #1461 + if ($start == $end) { + $start = trim($start) . ' 00:00:00'; + $end = trim($end) . ' ' . now()->format('H:i:s'); + } else { + $start = trim($start) . ' ' . now()->format('H:i:s'); + $end = trim($end) . ' ' . now()->format('H:i:s'); + } $this->whereBetween("$table.$column", [$start, $end]); } From df230b967d335c295403cfd11e60d3511cd6e05b Mon Sep 17 00:00:00 2001 From: dandiAW Date: Fri, 21 Feb 2025 15:56:49 +0800 Subject: [PATCH 3/3] Dashboard : hover label in bar and currency in bar --- .../Dashboard/Widget/ChartDisplay.vue | 72 +++++++++++++------ 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/resources/js/Components/DataDisplay/Dashboard/Widget/ChartDisplay.vue b/resources/js/Components/DataDisplay/Dashboard/Widget/ChartDisplay.vue index f46f347373..05c589fe22 100644 --- a/resources/js/Components/DataDisplay/Dashboard/Widget/ChartDisplay.vue +++ b/resources/js/Components/DataDisplay/Dashboard/Widget/ChartDisplay.vue @@ -135,32 +135,60 @@ function NumberDashboard(shop: any) { return route(shop?.name, shop?.parameters) } -const setChartOptions = () => ({ - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false, - }, - tooltip: { - callbacks: { - title: (tooltipItems) => { - const index = tooltipItems[0].dataIndex - return props.visual.label[index] +const setChartOptions = () => { + // Base chart options + const options: any = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + tooltip: { + callbacks: { + title: function (tooltipItems) { + if ( + props.visual && + props.visual.value.hover_labels && + tooltipItems.length > 0 + ) { + return props.visual.value.hover_labels[tooltipItems[0].dataIndex] + } + return tooltipItems[0].label + }, + label: (context) => { + const value = parseFloat(context.parsed.y ?? context.parsed) || 0 + const currencyCode = props.widget.currency_code + if (currencyCode) { + return locale.currencyFormat(currencyCode, value) + } + return locale.number(value) + }, }, + }, + }, + } - label: (context) => { - const index = context.dataIndex - const hoverLabels = props.visual.hoverLabels - if (hoverLabels && hoverLabels[index]) { - return hoverLabels[index] - } - return context.formattedValue + // Only apply y-axis currency formatting for bar charts + if (props.visual && props.visual.type === "bar") { + options.scales = { + y: { + ticks: { + callback: (value) => { + const numericValue = Number(value) || 0 + const currencyCode = props.widget.currency_code + if (currencyCode) { + return locale.currencyFormat(currencyCode, numericValue) + } + return locale.number(numericValue) + }, }, }, - }, - }, -}) + } + } + + return options +} // const chartLabels = ["1", "2", "3", "4", "5", "6", "7", "8"] // const chartData = [10, 20, 15, 25, 20, 18, 22, 10]