From 1d3dfa1fa4f378950de467dc6e64e373f2bfa53f Mon Sep 17 00:00:00 2001 From: snipe Date: Wed, 17 Sep 2025 21:43:17 +0100 Subject: [PATCH] Pull the acceptance stuff into the model --- .../Account/AcceptanceController.php | 248 +++++------------- app/Models/CheckoutAcceptance.php | 94 +++++++ 2 files changed, 161 insertions(+), 181 deletions(-) diff --git a/app/Http/Controllers/Account/AcceptanceController.php b/app/Http/Controllers/Account/AcceptanceController.php index 8cf970e675..be0391a123 100644 --- a/app/Http/Controllers/Account/AcceptanceController.php +++ b/app/Http/Controllers/Account/AcceptanceController.php @@ -13,11 +13,6 @@ use App\Models\Company; use App\Models\Contracts\Acceptable; use App\Models\Setting; use App\Models\User; -use App\Models\AssetModel; -use App\Models\Accessory; -use App\Models\License; -use App\Models\Component; -use App\Models\Consumable; use App\Notifications\AcceptanceAssetAcceptedNotification; use App\Notifications\AcceptanceAssetAcceptedToUserNotification; use App\Notifications\AcceptanceAssetDeclinedNotification; @@ -26,12 +21,9 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use App\Http\Controllers\SettingsController; -use Carbon\Carbon; use \Illuminate\Contracts\View\View; use \Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Log; -use TCPDF; use App\Helpers\Helper; class AcceptanceController extends Controller @@ -83,7 +75,12 @@ class AcceptanceController extends Controller public function store(Request $request, $id) : RedirectResponse { $acceptance = CheckoutAcceptance::find($id); + $assigned_user = User::find($acceptance->assigned_to_id); $settings = Setting::getSettings(); + $path_logo = ''; + $sig_filename=''; + + if (is_null($acceptance)) { return redirect()->route('account.accept')->with('error', trans('admin/hardware/message.does_not_exist')); } @@ -118,149 +115,71 @@ class AcceptanceController extends Controller Storage::makeDirectory('private_uploads/eula-pdfs', 775); } - $item = $acceptance->checkoutable_type::find($acceptance->checkoutable_id); - $checkout_type_shortname = strtolower(str_replace('App\Models\\', '', $acceptance->checkoutable_type)); - $pdf_filename = 'accepted-'.$acceptance->checkoutable_id.'-'.$checkout_type_shortname.'-eula-'.date('Y-m-d-h-i-s').'.pdf'; - $sig_filename=''; + + + + // If signatures are required, make sure we have one + if (Setting::getSettings()->require_accept_signature == '1') { + + // The item was accepted, check for a signature + if ($request->filled('signature_output')) { + $sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png'; + $data_uri = $request->input('signature_output'); + $encoded_image = explode(',', $data_uri); + $decoded_image = base64_decode($encoded_image[1]); + Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image); + + // No image data is present, kick them back. + // This mostly only applies to users on super-duper crapola browsers *cough* IE *cough* + } else { + return redirect()->back()->with('error', trans('general.shitty_browser')); + } + } + + + // Check for the PDF logo path and use that, otherwise use the regular logo path + if (!is_null($settings->acceptance_pdf_logo)) { + $path_logo = public_path() . '/uploads/' . $settings->acceptance_pdf_logo; + } elseif (!is_null($settings->logo)) { + $path_logo = public_path() . '/uploads/' . $settings->logo; + } + + + // Get the data array ready for the notifications and PDF generation + $data = [ + 'item_tag' => $item->asset_tag, + 'item_name' => $item->name, // this handles licenses seats, which don't have a 'name' field + 'item_model' => $item->model?->name, + 'item_serial' => $item->serial, + 'item_status' => $item->assetstatus?->name, + 'eula' => $item->getEula(), + 'note' => $request->input('note'), + 'check_out_date' => Helper::getFormattedDateObject($acceptance->created_at, 'datetime', false), + 'accepted_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false), + 'declined_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false), + 'assigned_to' => $assigned_user->display_name, + 'site_name' => $settings->site_name, + 'company_name' => $item->company?->name?? $settings->site_name, + 'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null, + 'logo' => $path_logo, + 'date_settings' => $settings->date_display_format, + 'admin' => auth()->user()->present()?->fullName, + 'qty' => $acceptance->qty ?? 1, + ]; + if ($request->input('asset_acceptance') == 'accepted') { - if (Setting::getSettings()->require_accept_signature == '1') { - // The item was accepted, check for a signature - if ($request->filled('signature_output')) { - $sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png'; - $data_uri = $request->input('signature_output'); - $encoded_image = explode(',', $data_uri); - $decoded_image = base64_decode($encoded_image[1]); - Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image); - - // No image data is present, kick them back. - // This mostly only applies to users on super-duper crapola browsers *cough* IE *cough* - } else { - return redirect()->back()->with('error', trans('general.shitty_browser')); - } - } - - $assigned_user = User::find($acceptance->assigned_to_id); - - - $path_logo = ""; - - // Check for the PDF logo path and use that, otherwise use the regular logo path - if (!is_null($settings->acceptance_pdf_logo)) { - $path_logo = public_path() . '/uploads/' . $settings->acceptance_pdf_logo; - } elseif (!is_null($settings->logo)) { - $path_logo = public_path() . '/uploads/' . $settings->logo; - } - - $data = [ - 'item_tag' => $item->asset_tag, - 'item_name' => $item->name, // this handles licenses seats, which don't have a 'name' field - 'item_model' => $item->model?->name, - 'item_serial' => $item->serial, - 'item_status' => $item->assetstatus?->name, - 'eula' => $item->getEula(), - 'note' => $request->input('note'), - 'check_out_date' => Helper::getFormattedDateObject($acceptance->created_at, 'datetime', false), - 'accepted_date' => Helper::getFormattedDateObject(now()->format('Y-m-d H:i:s'), 'datetime', false), - 'assigned_to' => $assigned_user->display_name, - 'site_name' => $settings->site_name, - 'company_name' => $item->company?->name?? $settings->site_name, - 'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null, - 'logo' => $path_logo, - 'date_settings' => $settings->date_display_format, - 'admin' => auth()->user()->present()?->fullName, - 'qty' => $acceptance->qty ?? 1, - ]; - - // set some language dependent data: - $lg = Array(); - $lg['a_meta_charset'] = 'UTF-8'; - $lg['w_page'] = 'page'; - - $pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false); - $pdf->setRTL(false); - $pdf->setLanguageArray($lg); - $pdf->SetFontSubsetting(true); - $pdf->SetCreator('Snipe-IT'); - $pdf->SetAuthor($data['assigned_to']); - $pdf->SetTitle('Asset Acceptance: '.$data['item_tag']); - $pdf->SetSubject('Asset Acceptance: '.$data['item_tag']); - $pdf->SetKeywords('Snipe-IT, assets, acceptance, eula, tos'); - $pdf->SetFont('dejavusans', '', 8, '', true); - $pdf->SetPrintHeader(false); - $pdf->SetPrintFooter(false); - - $pdf->AddPage(); - if ($data['logo']) { - $pdf->writeHTML('', true, 0, true, 0, ''); - } - - if ($data['site_name']) { - $pdf->writeHTML($data['site_name'], true, 0, true, 0, 'C'); - } - $pdf->Ln(); - $pdf->writeHTML(trans('general.date') . ': ' . Helper::getFormattedDateObject(now(), 'datetime', false), true, 0, true, 0, ''); - - if ($data['company_name'] != null) { - $pdf->writeHTML(trans('general.company') . ': ' . e($data['company_name']), true, 0, true, 0, ''); - } - if ($data['item_tag'] != null) { - $pdf->writeHTML(trans('general.asset_tag') . ': ' . e($data['item_tag']), true, 0, true, 0, ''); - } - if ($data['item_name'] != null) { - $pdf->writeHTML(trans('general.name') . ': ' . e($data['item_name']), true, 0, true, 0, ''); - } - if ($data['item_model'] != null) { - $pdf->writeHTML(trans('general.asset_model') . ': ' . e($data['item_model']), true, 0, true, 0, ''); - } - if ($data['item_serial'] != null) { - $pdf->writeHTML(trans('admin/hardware/form.serial').': '.e($data['item_serial']), true, 0, true, 0, ''); - } - $pdf->Ln(); - $pdf->writeHTML('
', true, 0, true, 0, ''); - - - // Break the EULA into lines based on newlines, and check each line for RTL or CJK characters - $eula_lines = preg_split("/\r\n|\n|\r/", $item->getEula()); - - foreach ($eula_lines as $eula_line) { - Helper::hasRtl($eula_line) ? $pdf->setRTL(true) : $pdf->setRTL(false); - Helper::isCjk($eula_line) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true); - - $pdf->writeHTML(Helper::parseEscapedMarkedown($eula_line), true, 0, true, 0, ''); - } - $pdf->Ln(); - $pdf->Ln(); - $pdf->setRTL(false); - $pdf->writeHTML('

', true, 0, true, 0, ''); - - if ($data['note'] != null) { - Helper::isCjk($data['note']) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true); - $pdf->writeHTML(trans('general.notes') . ': ' . e($data['note']), true, 0, true, 0, ''); - $pdf->Ln(); - } - - if ($data['signature'] != null) { - $pdf->writeHTML('', true, 0, true, 0, ''); - $pdf->writeHTML('
', true, 0, true, 0, ''); - } - - $pdf->writeHTML(trans('general.assignee').': '.e($data['assigned_to']), true, 0, true, 0, ''); - $pdf->writeHTML(trans('general.assigned_date').': '.e($data['check_out_date']), true, 0, true, 0, ''); - $pdf->writeHTML(trans('general.accepted_date').': '.e($data['accepted_date']), true, 0, true, 0, ''); - - // close line height div - $pdf->writeHTML('', true, 0, true, 0, ''); - - $pdf_content = $pdf->Output($pdf_filename, 'S'); + $pdf_filename = 'accepted-'.$acceptance->checkoutable_id.'-'.$acceptance->display_checkoutable_type.'-eula-'.date('Y-m-d-h-i-s').'.pdf'; + // Generate the PDF content + $pdf_content = $acceptance->generateAcceptancePdf($data, $acceptance); Storage::put('private_uploads/eula-pdfs/' .$pdf_filename, $pdf_content); - - $acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note')); + // Log the acceptance + // $acceptance->accept($sig_filename, $item->getEula(), $pdf_filename, $request->input('note')); // Send the PDF to the signing user if (($request->input('send_copy') == '1') && ($assigned_user->email !='')) { @@ -282,45 +201,9 @@ class AcceptanceController extends Controller $return_msg = trans('admin/users/message.accepted'); - // Item was not accepted + // Item was declined } else { - if (Setting::getSettings()->require_accept_signature == '1') { - - // The item was declined, check for a signature - if ($request->filled('signature_output')) { - $sig_filename = 'siglog-' . Str::uuid() . '-' . date('Y-m-d-his') . '.png'; - $data_uri = $request->input('signature_output'); - $encoded_image = explode(',', $data_uri); - $decoded_image = base64_decode($encoded_image[1]); - Storage::put('private_uploads/signatures/' . $sig_filename, (string)$decoded_image); - - // No image data is present, kick them back. - // This mostly only applies to users on super-duper crapola browsers *cough* IE *cough* - } else { - return redirect()->back()->with('error', trans('general.shitty_browser')); - } - } - - // Format the data to send the declined notification - $branding_settings = SettingsController::getPDFBranding(); - $assigned_to = User::find($acceptance->assigned_to_id)->present()->fullName; - - $data = [ - 'item_tag' => $item->asset_tag, - 'item_model' => $item->model ? $item->model->name : $item->display_name, - 'item_serial' => $item->serial, - 'item_status' => $item->assetstatus?->name, - 'note' => $request->input('note'), - 'declined_date' => Carbon::parse($acceptance->declined_at)->format('Y-m-d'), - 'signature' => ($sig_filename) ? storage_path() . '/private_uploads/signatures/' . $sig_filename : null, - 'assigned_to' => $assigned_to, - 'company_name' => $branding_settings->site_name, - 'date_settings' => $branding_settings->date_display_format, - 'qty' => $acceptance->qty ?? 1, - ]; - - for ($i = 0; $i < ($acceptance->qty ?? 1); $i++) { $acceptance->decline($sig_filename, $request->input('note')); } @@ -331,6 +214,8 @@ class AcceptanceController extends Controller $return_msg = trans('admin/users/message.declined'); } + + // Send an email notification if one is requested if ($acceptance->alert_on_response_id) { try { $recipient = User::find($acceptance->alert_on_response_id); @@ -349,9 +234,10 @@ class AcceptanceController extends Controller Log::warning($e); } } - return redirect()->to('account/accept')->with('success', $return_msg); } + + } diff --git a/app/Models/CheckoutAcceptance.php b/app/Models/CheckoutAcceptance.php index 2ad6c0e558..4cfd79d092 100644 --- a/app/Models/CheckoutAcceptance.php +++ b/app/Models/CheckoutAcceptance.php @@ -2,11 +2,14 @@ namespace App\Models; +use App\Helpers\Helper; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Notifications\Notifiable; +use TCPDF; class CheckoutAcceptance extends Model { @@ -153,4 +156,95 @@ class CheckoutAcceptance extends Model { return $query->whereNull('accepted_at')->whereNotNull('declined_at'); } + + protected function displayCheckoutableType(): Attribute + { + return Attribute:: make( + get: fn(mixed $value) => strtolower(str_replace('App\Models\\', '', $this->checkoutable_type)), + ); + } + + public function generateAcceptancePdf($data, $pdf_filename) { + + // set some language dependent data: + $lg = Array(); + $lg['a_meta_charset'] = 'UTF-8'; + $lg['w_page'] = 'page'; + + $pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8', false); + $pdf->setRTL(false); + $pdf->setLanguageArray($lg); + $pdf->SetFontSubsetting(true); + $pdf->SetCreator('Snipe-IT Asset Management System'); + $pdf->SetAuthor($data['assigned_to']); + $pdf->SetTitle('Asset Acceptance: '.$data['item_tag']); + $pdf->SetSubject('Asset Acceptance: '.$data['item_tag']); + $pdf->SetKeywords('Snipe-IT, assets, acceptance, eula, tos'); + $pdf->SetFont('dejavusans', '', 8, '', true); + $pdf->SetPrintHeader(false); + $pdf->SetPrintFooter(false); + + $pdf->AddPage(); + if ($data['logo']) { + $pdf->writeHTML('', true, 0, true, 0, ''); + } + + if ($data['site_name']) { + $pdf->writeHTML($data['site_name'], true, 0, true, 0, 'C'); + } + $pdf->Ln(); + $pdf->writeHTML(trans('general.date') . ': ' . Helper::getFormattedDateObject(now(), 'datetime', false), true, 0, true, 0, ''); + + if ($data['company_name'] != null) { + $pdf->writeHTML(trans('general.company') . ': ' . e($data['company_name']), true, 0, true, 0, ''); + } + if ($data['item_tag'] != null) { + $pdf->writeHTML(trans('general.asset_tag') . ': ' . e($data['item_tag']), true, 0, true, 0, ''); + } + if ($data['item_name'] != null) { + $pdf->writeHTML(trans('general.name') . ': ' . e($data['item_name']), true, 0, true, 0, ''); + } + if ($data['item_model'] != null) { + $pdf->writeHTML(trans('general.asset_model') . ': ' . e($data['item_model']), true, 0, true, 0, ''); + } + if ($data['item_serial'] != null) { + $pdf->writeHTML(trans('admin/hardware/form.serial').': '.e($data['item_serial']), true, 0, true, 0, ''); + } + $pdf->Ln(); + $pdf->writeHTML('
', true, 0, true, 0, ''); + + + // Break the EULA into lines based on newlines, and check each line for RTL or CJK characters + $eula_lines = preg_split("/\r\n|\n|\r/", $data['eula']); + + foreach ($eula_lines as $eula_line) { + Helper::hasRtl($eula_line) ? $pdf->setRTL(true) : $pdf->setRTL(false); + Helper::isCjk($eula_line) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true); + + $pdf->writeHTML(Helper::parseEscapedMarkedown($eula_line), true, 0, true, 0, ''); + } + $pdf->Ln(); + $pdf->Ln(); + $pdf->setRTL(false); + $pdf->Ln(); + + if ($data['signature'] != null) { + $pdf->writeHTML('', true, 0, true, 0, ''); + $pdf->writeHTML('
', true, 0, true, 0, ''); + } + + if ($data['note'] != null) { + Helper::isCjk($data['note']) ? $pdf->SetFont('cid0cs', '', 9) : $pdf->SetFont('dejavusans', '', 8, '', true); + $pdf->writeHTML(trans('general.notes') . ': ' . e($data['note']), true, 0, true, 0, ''); + $pdf->Ln(); + } + + $pdf->writeHTML(trans('general.assignee').': '.e($data['assigned_to']), true, 0, true, 0, ''); + $pdf->writeHTML(trans('general.assigned_date').': '.e($data['check_out_date']), true, 0, true, 0, ''); + $pdf->writeHTML(trans('general.accepted_date').': '.e($data['accepted_date']), true, 0, true, 0, ''); + + return $pdf->Output($pdf_filename, 'S'); + + + } }