Files
CKB/resources/views/transaction/index.blade.php

2808 lines
126 KiB
PHP
Executable File

@extends('layouts.frontapp')
@php
use App\Models\Dealer;
use Illuminate\Support\Facades\Auth;
@endphp
{{-- @section('contentHead')
<div class="kt-subheader kt-grid__item" id="kt_subheader">
<div class="kt-container kt-container--fluid ">
<div class="kt-subheader__main">
<h3 class="kt-subheader__title"> Transaksi </h3>
</div>
<div class="kt-subheader__toolbar" style="width: 1000px;">
<a href="{{ route('transaction.create') }}" id="addBtn" class="btn btn-label-success btn-bold" data-url="" data-redirect="">Tambah Transaksi</a>
</div>
</div>
</div>
@endsection --}}
@section('styles')
<style>
button.disabled {
cursor: not-allowed !important;
pointer-events: none;
}
.table-responsive {
max-height: 100%;
overflow-y: auto;
}
.text-success {
color: #28a745 !important;
}
.text-danger {
color: #dc3545 !important;
}
.text-bold {
font-weight: bold;
}
.system-stock {
font-weight: 600;
color: #007bff;
}
.table-bordered th,
.table-bordered td {
border: 1px solid #dee2e6;
vertical-align: middle;
padding: 12px 8px;
}
.table thead th {
background-color: #f8f9fa;
color: #495057;
font-weight: 600;
font-size: 14px;
border-bottom: 2px solid #dee2e6;
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: rgba(0,0,0,.02);
}
.physical-stock {
font-weight: 500;
border: 2px solid #e9ecef;
transition: border-color 0.15s ease-in-out;
}
.physical-stock:focus {
border-color: #ffc107;
box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.25);
}
.difference {
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
background-color: #f8f9fa;
display: inline-block;
min-width: 60px;
}
.btn-lg {
padding: 12px 20px;
font-size: 16px;
font-weight: 600;
letter-spacing: 0.5px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.btn-lg:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
}
.physical-stock.is-invalid {
border-color: #dc3545;
background-color: #fff5f5;
}
.physical-stock.is-invalid:focus {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
.quantity-input-mutasi.is-invalid {
border-color: #dc3545;
background-color: #fff5f5;
}
.quantity-input-mutasi.is-invalid:focus {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
.available-stock-mutasi {
font-weight: 600;
}
/* Styles for receive mutations table */
#receiveMutationsTable {
font-size: 14px;
}
#receiveMutationsTable th {
background-color: #f8f9fa;
font-weight: 600;
text-align: center;
vertical-align: middle;
}
#receiveMutationsTable td {
vertical-align: middle;
}
.btn-detail {
font-size: 12px;
padding: 4px 8px;
}
.status-badge {
font-size: 11px;
padding: 4px 8px;
border-radius: 12px;
font-weight: 600;
}
.mutation-detail-table {
font-size: 14px;
}
.mutation-detail-table th {
background-color: #f8f9fa;
font-weight: 600;
}
.quantity-approved-input {
text-align: center;
font-weight: 600;
}
.quantity-approved-input.is-invalid {
border-color: #dc3545;
background-color: #fff5f5;
}
.mutation-detail-table textarea {
font-size: 12px;
resize: vertical;
}
.mutation-detail-table input {
font-size: 14px;
}
.bg-warning-light {
background-color: #fff3cd !important;
border: 1px solid #ffeaa7;
}
.form-control-plaintext.border {
background-color: #f8f9fa;
font-style: normal;
}
.alert ul {
padding-left: 1.2rem;
}
.alert ul li {
margin-bottom: 0.25rem;
}
/* Select2 styling for mutasi form */
.select2-container {
width: 100% !important;
}
.select2-container--default .select2-selection--single {
height: 38px;
border: 1px solid #e2e5ec;
border-radius: 4px;
display: flex;
align-items: center;
padding: 0;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 36px;
padding-left: 12px;
padding-right: 30px;
color: #74788d;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: calc(100% - 30px);
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #74788d;
line-height: 36px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 36px;
right: 8px;
top: 1px;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #74788d transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0;
}
/* Hide clear button completely */
.select2-container--default .select2-selection--single .select2-selection__clear {
display: none !important;
}
.select2-dropdown {
border: 1px solid #e2e5ec;
border-radius: 4px;
z-index: 9999 !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
.select2-results__option {
padding: 8px 12px;
line-height: 1.4;
}
.select2-results__option--highlighted {
background-color: #5d78ff;
color: white;
}
.select2-container--default.select2-container--focus .select2-selection--single {
border-color: #5d78ff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(93, 120, 255, 0.25);
}
/* Select2 for table cells - adjust width */
#products-table-mutasi .select2-container {
width: 100% !important;
max-width: 100%;
}
/* Style for placeholder options */
select option[disabled] {
color: #6c757d;
font-style: italic;
background-color: #f8f9fa;
}
/* Prevent auto-selection of first option */
select:focus option:first-child {
background-color: #f8f9fa;
}
/* Required field styling */
.form-control[required]:not(:placeholder-shown):valid {
border-color: #28a745;
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
}
.form-control[required]:not(:placeholder-shown):invalid {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
/* Required field indicator */
.form-control[required]::placeholder {
color: #6c757d;
}
/* Service Advisor required styling */
select[name="user_sa_id"][required] option:first-child {
color: #6c757d;
font-style: italic;
}
/* Required field labels */
.form-group label:after {
content: " *";
color: #dc3545;
font-weight: bold;
}
/* Required field focus styling */
.form-control[required]:focus {
border-color: #5d78ff;
box-shadow: 0 0 0 0.2rem rgba(93, 120, 255, 0.25);
}
/* Invalid field styling */
.form-control.is-invalid {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
/* Valid field styling */
.form-control.is-valid {
border-color: #28a745;
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
}
</style>
@endsection
@section('content')
<div class="mobile-container">
<div class="container">
<div class="row mb-4 mt-4">
<div class="col-8">
{{-- <h5 class="text-center mt-4">Cipta Kreasi Baru</h5> --}}
<a href="/"><img src="{{ asset('logo-ckb.png') }}" style="width: 100%" alt="LOGO CKB"></a>
</div>
<div class="col-4 text-right my-auto">
<a class="btn btn-sm btn-danger mt-3" style="background: red !important;" href="{{ route('logout') }}" onclick="logout(event)">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</div>
<div class="row">
<div class="col-12">
<!--begin::Portlet-->
<div class="kt-portlet">
<div class="kt-portlet__head">
<div class="kt-portlet__head-label">
<span>{{ $mechanic->name }}</span>
</div>
<div class="kt-portlet__head-toolbar">
<span class="float-right">{{ $now }}</span>
</div>
</div>
<div class="kt-portlet__body">
<div class="row">
<div class="col-8">
<b>Dealer {{ $mechanic->dealer_name }}</b><br><br>
<a href="#">Total {{ $count_transaction_dealers }} Pekerjaan terkirim pada dealer</a><br>
<a href="#">Anda telah posting {{ $count_transaction_users }} pekerjaan</a>
</div>
<div class="col-4">
<div class="text-center mt-2">
<a href="{{ route('transaction.lists') }}">
<i style="font-size: 16px;" class="fa fa-eye"></i>
<p>Laporan harian</p>
</a>
</div>
<div class="text-center mt-2">
<a href="{{ route('transaction.recap') }}">
<i style="font-size: 16px;" class="fa fa-eye"></i>
<p>Rekap Laporan</p>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<!--begin::Portlet-->
<div class="kt-portlet">
<div class="kt-portlet__body">
<!-- Main Tabs -->
<ul class="nav nav-tabs nav-tabs-line nav-tabs-line-primary" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#transaksi">Transaksi</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#stock">Stock</a>
</li>
</ul>
<div class="tab-content">
<!-- Tab Transaksi -->
<div class="tab-pane active" id="transaksi" role="tabpanel">
<!-- Sub Tabs untuk Transaksi -->
<ul class="nav nav-tabs nav-tabs-line nav-tabs-line-success mt-3" role="tablist">
<li class="nav-item">
<a class="nav-link @if(old('form') == 'wash') @else active @endif" data-toggle="tab" href="#form-kerja">Form Kerja</a>
</li>
<li class="nav-item">
<a class="nav-link @if(old('form') == 'wash') active @endif" data-toggle="tab" href="#form-cuci">Form Cuci</a>
</li>
</ul>
<div class="tab-content mt-3">
<!-- Form Kerja -->
<div class="tab-pane @if(old('form') == 'wash') @else active @endif" id="form-kerja" role="tabpanel">
<form action="{{ route('transaction.store') }}" method="POST" id="workForm">
@csrf
<input type="hidden" name="form" value="work">
<input type="hidden" name="mechanic_id" value="{{ $mechanic->id }}">
<input type="hidden" name="dealer_id" value="{{ $mechanic->dealer_id }}">
<!-- Stock Error Display -->
@if($errors->has('stock'))
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<strong><i class="fa fa-exclamation-triangle"></i> Peringatan Stock:</strong>
<br>{!! $errors->first('stock') !!}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
@if($errors->has('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong><i class="fa fa-times-circle"></i> Error:</strong>
{{ $errors->first('error') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
<div class="form-group row">
<div class="col-6">
<label>No. SPK</label>
<input type="text" name="spk_no" class="form-control @if(old('form') == 'work') @error('spk_no') is-invalid @enderror @endif" value="{{ old('spk_no') }}" placeholder="No. SPK" required>
@if(old('form') == 'work')
@error('spk_no')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
<div class="col-6">
<label>No. Polisi</label>
<input type="text" name="police_number" class="form-control @if(old('form') == 'work') @error('police_number') is-invalid @enderror" @endif value="{{ old('police_number') }}" placeholder="No. Polisi" required>
@if(old('form') == 'work')
@error('police_number')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
</div>
<div class="form-group row">
<div class="col-6">
<label>Warranty</label>
<select name="warranty" class="form-control @if(old('form') == 'work') @error('warranty') is-invalid @enderror @endif">
<option selected>Warranty</option>
<option value="1" @if(old('form') == 'work') @if(old('warranty') == 1) selected @endif @endif>Ya</option>
<option value="0" @if(old('form') == 'work') @if(old('warranty') == 0) selected @endif @endif>Tidak</option>
</select>
@if(old('form') == 'work')
@error('warranty')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
<div class="col-6">
<label>Tanggal Pekerjaan</label>
<input type="text" name="date" id="date-work" required class="form-control @if(old('form') == 'work') @error('date') is-invalid @enderror @endif" placeholder="Tanggal Pekerjaan">
@if(old('form') == 'work')
@error('date')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
</div>
<div class="form-group mt-4">
<label>Service Advisor</label>
<select name="user_sa_id" class="form-control @if(old('form') == 'work') @error('user_sa_id') is-invalid @enderror @endif" required>
<option value="" selected>Service Advisor</option>
@foreach ($user_sas as $user_sa)
<option @if(old('form') == 'work') @if($user_sa->id == old('user_sa_id')) selected @enderror @endif value="{{ $user_sa->id }}">{{ $user_sa->name }}</option>
@endforeach
</select>
@error('user_sa_id')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
</div>
<input type="hidden" name="category" value="work">
<div class="work_multirow">
@if (old('work_id') && old('form') == 'work')
{{-- @php
dd($errors->all());
@endphp --}}
<input type="hidden" class="work_field_counter" value="{{ count(old('work_id')) }}">
@for ($i = 0; $i < count(old('work_id')); $i++)
<div class="form-group row" id="work_field{{ $i+1 }}">
<div class="col-6">
<select name="work_id[]" id="work_work{{ $i+1 }}" class="form-control @error('work_id.'.$i) is-invalid @enderror">
<option value="" disabled>Pekerjaan</option>
@foreach ($work_works as $work)
<option value="{{ $work->id }}" @if($work->id == old('work_id.'.$i)) selected @endif>{{ $work->name }}</option>
@endforeach
</select>
@error('work_id.'.$i)
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
</div>
<div class="col-4">
<input type="number" class="form-control @error('quantity.'.$i) is-invalid @enderror" value="{{ old('quantity.'.$i) }}" name="quantity[]" placeholder="Qty">
@error('quantity.'.$i)
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
</div>
<div class="col-2">
<button class="btn btn-danger work-btn-remove float-right" style="width: 100%;" type="button" id="workRemove1" onclick="removeFormField('work', 1)">X</button>
</div>
</div>
@endfor
@else
<input type="hidden" class="work_field_counter" value="5">
<div class="form-group row" id="work_field1">
<div class="col-6">
<select name="work_id[]" id="work_work1" class="form-control">
<option value="" selected disabled>Pekerjaan</option>
@foreach ($work_works as $work)
<option value="{{ $work->id }}">{{ $work->name }}</option>
@endforeach
</select>
</div>
<div class="col-4">
<input type="number" class="form-control" name="quantity[]" placeholder="Qty">
</div>
<div class="col-2">
<button class="btn btn-danger work-btn-remove float-right" style="width: 100%;" type="button" id="workRemove1" onclick="removeFormField('work', 1)">X</button>
</div>
</div>
<div class="form-group row" id="work_field2">
<div class="col-6">
<select name="work_id[]" id="work_work2" class="form-control">
<option value="" selected disabled>Pekerjaan</option>
@foreach ($work_works as $work)
<option value="{{ $work->id }}">{{ $work->name }}</option>
@endforeach
</select>
</div>
<div class="col-4">
<input type="number" class="form-control" name="quantity[]" placeholder="Qty">
</div>
<div class="col-2">
<button class="btn btn-danger work-btn-remove float-right" style="width: 100%;" type="button" id="workRemove2" onclick="removeFormField('work', 2)">X</button>
</div>
</div>
<div class="form-group row" id="work_field3">
<div class="col-6">
<select name="work_id[]" id="work_work3" class="form-control">
<option value="" selected disabled>Pekerjaan</option>
@foreach ($work_works as $work)
<option value="{{ $work->id }}">{{ $work->name }}</option>
@endforeach
</select>
</div>
<div class="col-4">
<input type="number" class="form-control" name="quantity[]" placeholder="Qty">
</div>
<div class="col-2">
<button class="btn btn-danger work-btn-remove float-right" style="width: 100%;" type="button" id="workRemove3" onclick="removeFormField('work', 3)">X</button>
</div>
</div>
<div class="form-group row" id="work_field4">
<div class="col-6">
<select name="work_id[]" id="work_work4" class="form-control">
<option value="" selected disabled>Pekerjaan</option>
@foreach ($work_works as $work)
<option value="{{ $work->id }}">{{ $work->name }}</option>
@endforeach
</select>
</div>
<div class="col-4">
<input type="number" class="form-control" name="quantity[]" placeholder="Qty">
</div>
<div class="col-2">
<button class="btn btn-danger work-btn-remove float-right" style="width: 100%;" type="button" id="workRemove4" onclick="removeFormField('work', 4)">X</button>
</div>
</div>
<div class="form-group row" id="work_field5">
<div class="col-6">
<select name="work_id[]" id="work_work5" class="form-control">
<option value="" selected disabled>Pekerjaan</option>
@foreach ($work_works as $work)
<option value="{{ $work->id }}">{{ $work->name }}</option>
@endforeach
</select>
</div>
<div class="col-4">
<input type="number" class="form-control" name="quantity[]" placeholder="Qty">
</div>
<div class="col-2">
<button class="btn btn-danger work-btn-remove float-right" style="width: 100%;" type="button" id="workRemove5" onclick="removeFormField('work', 5)">X</button>
</div>
</div>
@endif
<div class="row">
<div class="col-10"></div>
<div class="col-2">
<button class="btn mb-4 btn-sm btn-primary float-right btn-add-field-work" style="width: 100%;" onclick="addFormFieldWithStockCheck('work'); return false;">+</button>
</div>
</div>
</div>
<button class="btn btn-brand button-save" style="display: block; width: 100%;">Simpan</button>
</form>
</div>
<!-- Form Cuci -->
<div class="tab-pane @if(old('form') == 'wash') active @endif" id="form-cuci" role="tabpanel">
<form action="{{ route('transaction.store') }}" method="POST" id="washForm">
@csrf
<input type="hidden" name="form" value="wash">
<input type="hidden" name="mechanic_id" value="{{ $mechanic->id }}">
<input type="hidden" name="dealer_id" value="{{ $mechanic->dealer_id }}">
<div class="form-group row">
<div class="col-6">
<label>No. SPK</label>
<input type="text" name="spk_no" class="form-control @if(old('form') == 'wash') @error('spk_no') is-invalid @enderror @endif" value="{{ old('spk_no') }}" placeholder="No. SPK" required>
@if(old('form') == 'wash')
@error('spk_no')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
<div class="col-6">
<label>No. Polisi</label>
<input type="text" name="police_number" class="form-control @if(old('form') == 'wash') @error('police_number') is-invalid @enderror @endif" value="{{ old('police_number') }}" placeholder="No. Polisi" required>
@if(old('form') == 'wash')
@error('police_number')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
</div>
<div class="form-group row">
<div class="col-6">
<label>Warranty</label>
<select name="warranty" class="form-control @if(old('form') == 'wash') @error('warranty') is-invalid @enderror @endif">
<option selected>Warranty</option>
<option value="1" @if(old('form') == 'wash') @if(old('warranty') == 1) selected @endif @endif>Ya</option>
<option value="0" @if(old('form') == 'wash') @if(old('warranty') == 0) selected @endif @endif>Tidak</option>
</select>
@error('warranty')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
</div>
<div class="col-6">
<label>Tanggal Pekerjaan</label>
<input type="text" id="date-wash" name="date" class="form-control @if(old('form') == 'wash') @error('date') is-invalid @enderror @endif" placeholder="Tanggal Pekerjaan" required>
@if(old('form') == 'wash')
@error('date')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
</div>
<div class="form-group mt-4">
<label>Service Advisor</label>
<select name="user_sa_id" class="form-control @if(old('form') == 'wash') @error('user_sa_id') is-invalid @enderror @endif" required>
<option value="" selected>Service Advisor</option>
@foreach ($user_sas as $user_sa)
<option @if(old('form') == 'wash') @if($user_sa->id == old('user_sa_id')) selected @enderror @endif value="{{ $user_sa->id }}">{{ $user_sa->name }}</option>
@endforeach
</select>
@if(old('form') == 'wash')
@error('user_sa_id')
<span class="invalid-feedback" role="alert">
<strong>{!! $message !!}</strong>
</span>
@enderror
@endif
</div>
<input type="hidden" name="category" value="work">
<div class="form-group">
<label>Pekerjaan</label>
<input type="text" class="form-control" disabled value="{{ $wash_work->name }}">
<input type="hidden" class="form-control" name="work_id[]" value="{{ $wash_work->id }}">
<input type="hidden" class="form-control" name="quantity[]" value="1">
</div>
<button class="btn btn-brand button-save" style="display: block; width: 100%;">Simpan</button>
</form>
</div>
</div>
</div>
<!-- Tab Stock -->
<div class="tab-pane" id="stock" role="tabpanel">
<!-- Sub Tabs untuk Stock -->
<ul class="nav nav-tabs nav-tabs-line nav-tabs-line-warning mt-3" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#opname">Opname</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#mutasi">Mutasi</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#penerimaan">Penerimaan Mutasi</a>
</li>
</ul>
<div class="tab-content mt-3">
<!-- Form Opname -->
<div class="tab-pane active" id="opname" role="tabpanel">
<form action="{{ route('opnames.store') }}" method="POST" id="opnameForm">
@csrf
<input type="hidden" name="form" value="opname">
<input type="hidden" name="user_id" value="{{ $mechanic->id }}">
<input type="hidden" name="dealer_id" value="{{ $mechanic->dealer_id }}">
<div class="form-group">
<label>Tanggal Opname <small class="text-muted">(Default: Hari ini)</small></label>
<input type="text" name="opname_date" id="date-opname" class="form-control @error('opname_date') is-invalid @enderror" value="{{ old('opname_date', date('Y-m-d')) }}" placeholder="YYYY-MM-DD">
@error('opname_date')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<div class="form-group">
<label>Keterangan</label>
<textarea name="description" class="form-control @error('description') is-invalid @enderror" rows="3" placeholder="Keterangan opname">{{ old('description') }}</textarea>
@error('description')
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</div>
<!-- List Produk dengan Stock -->
<div class="table-responsive">
<table class="table table-bordered table-striped">
<thead class="thead-light">
<tr>
<th class="text-center">Produk</th>
<th class="text-center">Dealer</th>
<th class="text-center">Stock Sistem</th>
<th class="text-center">Stock Fisik</th>
<th class="text-center">Selisih</th>
</tr>
</thead>
<tbody>
{{-- Dealer/Mechanic - Show products for current dealer only --}}
@foreach($products as $product)
@php
$stock = $product->stocks->first();
$currentStock = $stock ? $stock->quantity : 0;
@endphp
<tr>
<td class="text-center">{{ $product->name }}</td>
<td class="text-center">{{ $mechanic->dealer_name }}</td>
<td class="text-center">
<span class="system-stock">{{ number_format($currentStock, 2) }}</span>
<input type="hidden" name="product_id[]" value="{{ $product->id }}">
<input type="hidden" name="dealer_id_stock[]" value="{{ $mechanic->dealer_id }}">
<input type="hidden" name="system_stock[]" value="{{ $currentStock }}">
</td>
<td>
<input type="number" class="form-control physical-stock @error('physical_stock.'.$loop->index) is-invalid @enderror" name="physical_stock[]" step="0.01" placeholder="0.00" data-system="{{ $currentStock }}" value="{{ old('physical_stock.'.$loop->index) }}">
@error('physical_stock.'.$loop->index)
<div class="invalid-feedback">
{{ $message }}
</div>
@enderror
</td>
<td class="text-center">
<span class="difference text-bold">0.00</span>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mt-4 mb-3">
<button id="btn-save-opname" class="btn btn-warning btn-lg d-block w-100 mt-2">
Simpan Opname
</button>
</div>
</form>
</div>
<!-- Form Mutasi -->
<div class="tab-pane" id="mutasi" role="tabpanel">
<form action="{{ route('mutations.store') }}" method="POST" id="mutasiForm">
@csrf
<input type="hidden" name="from_dealer_id" value="{{ $mechanic->dealer_id }}">
<input type="hidden" name="from_transaction_page" value="1">
<div class="form-group">
<label>Dealer Asal</label>
<input type="text" class="form-control" value="{{ $mechanic->dealer_name }}" disabled>
<small class="text-muted">Otomatis terdeteksi dari akun Anda</small>
</div>
<div class="form-group">
<label for="to_dealer_id">Dealer Tujuan <span class="text-danger">*</span></label>
<select name="to_dealer_id" id="to_dealer_id" class="form-control" required>
<option value="">Pilih Dealer Tujuan</option>
@php
$dealers = \App\Models\Dealer::where('id', '!=', $mechanic->dealer_id)->get();
@endphp
@foreach($dealers as $dealer)
<option value="{{ $dealer->id }}">{{ $dealer->name }}</option>
@endforeach
</select>
</div>
<div class="form-group">
<div class="d-flex justify-content-between align-items-center mb-3">
<label class="form-label mb-0">Detail Produk <span class="text-danger">*</span></label>
<button type="button" class="btn btn-success btn-sm" id="add-product-mutasi">
<i class="fa fa-plus"></i> Tambah Produk
</button>
</div>
<div class="table-responsive">
<table class="table table-bordered table-sm" id="products-table-mutasi">
<thead>
<tr>
<th width="40%">Produk</th>
<th width="20%">Stock Tersedia</th>
<th width="25%">Quantity</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody id="products-tbody-mutasi">
<tr class="product-row-mutasi" data-index="0">
<td>
<select name="products[0][product_id]" class="form-control product-select-mutasi" required>
<option value="">Pilih Produk</option>
@foreach($products as $product)
<option value="{{ $product->id }}">{{ $product->name }}</option>
@endforeach
</select>
</td>
<td class="text-center">
<span class="available-stock-mutasi text-muted">-</span>
</td>
<td>
<input type="number"
name="products[0][quantity_requested]"
class="form-control quantity-input-mutasi"
min="0.01"
step="0.01"
placeholder="0"
required>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-product-mutasi" disabled>
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="mt-4 mb-3">
<button id="btn-save-mutasi" type="submit" class="btn btn-warning btn-lg d-block w-100 mt-2">
Kirim Mutasi
</button>
</div>
</form>
</div>
<!-- Tab Penerimaan Mutasi -->
<div class="tab-pane" id="penerimaan" role="tabpanel">
<div class="mt-3">
<h6 class="mb-3">Daftar Mutasi yang Perlu Diterima & Disetujui</h6>
<div class="table-responsive">
<table class="table table-bordered table-hover" id="receiveMutationsTable">
<thead class="thead-light">
<tr>
<th width="15%">No. Mutasi</th>
<th width="20%">Dealer Asal</th>
<th width="20%">Dealer Tujuan</th>
<th width="12%">Status</th>
<th width="8%">Total Item</th>
<th width="10%">Tanggal</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody>
<!-- Data will be loaded via DataTables AJAX -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!--end::Portlet-->
</div>
</div>
<input type="hidden" name="ajax_work_url" value="{{ route('transaction.workcategory', ':id') }}">
<input type="hidden" name="mechanic_id" value="{{ $mechanic->id }}">
<input type="hidden" name="dealer_id" value="{{ $mechanic->dealer_id }}">
</div>
</div>
<!-- Modal Detail Mutasi -->
<div class="modal fade" id="mutationDetailModal" tabindex="-1" role="dialog" aria-labelledby="mutationDetailModalLabel">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="mutationDetailModalLabel">Detail & Penerimaan Mutasi</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="receiveMutationForm" action="" method="POST">
@csrf
<input type="hidden" name="from_transaction_page" value="1">
<div class="modal-body">
<div id="mutationDetailContent">
<div class="text-center">
<i class="fa fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">Memuat data...</p>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
<button type="submit" class="btn btn-success" id="receiveButton" style="display: none;">
<i class="fa fa-check"></i> Terima
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('javascripts')
<script>
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
// Global variables for stock checking
let dealerId = {{ $mechanic->dealer_id }};
let stockWarnings = {};
// Function to check stock availability for selected work
function checkStockAvailability(workId, quantity, callback) {
if (!workId || !quantity || quantity < 1) {
if (callback) callback(null);
return;
}
$.ajax({
url: "{{ route('transaction.check-stock') }}",
method: 'POST',
data: {
work_id: workId,
dealer_id: dealerId,
quantity: quantity,
_token: $('meta[name="csrf-token"]').attr('content')
},
success: function(response) {
if (callback) callback(response.data);
},
error: function(xhr) {
console.error('Error checking stock:', xhr);
if (callback) callback(null);
}
});
}
// Function to display stock warning
function displayStockWarning(fieldId, stockData) {
// Remove existing warning
$(`#stock-warning-${fieldId}`).remove();
if (!stockData || stockData.available) {
return; // No warning needed
}
let warningHtml = `
<div id="stock-warning-${fieldId}" class="alert alert-warning mt-2 mb-0" style="font-size: 12px;">
<strong><i class="fa fa-exclamation-triangle"></i> Peringatan Stock:</strong> ${stockData.message}
<ul class="mb-0 mt-1" style="font-size: 11px;">
`;
stockData.details.forEach(function(detail) {
if (!detail.is_available) {
warningHtml += `
<li>${detail.product_name}: Butuh ${detail.required_quantity}, Tersedia ${detail.available_stock}</li>
`;
}
});
warningHtml += `
</ul>
</div>
`;
$(`#work_field${fieldId.replace('work_work', '')}`).append(warningHtml);
}
// Function to handle work selection change
function handleWorkSelectionChange(selectElement) {
let workId = $(selectElement).val();
let fieldId = $(selectElement).attr('id');
let quantityInput = $(selectElement).closest('.form-group').find('input[name="quantity[]"]');
let quantity = parseInt(quantityInput.val()) || 1;
if (workId) {
checkStockAvailability(workId, quantity, function(stockData) {
displayStockWarning(fieldId, stockData);
// Store warning data for form submission validation
if (stockData && !stockData.available) {
stockWarnings[fieldId] = stockData;
} else {
delete stockWarnings[fieldId];
}
});
} else {
// Remove warning when no work selected
$(`#stock-warning-${fieldId}`).remove();
delete stockWarnings[fieldId];
}
}
// Function to handle quantity change
function handleQuantityChange(quantityInput) {
let workSelect = $(quantityInput).closest('.form-group').find('select[name="work_id[]"]');
let workId = workSelect.val();
let quantity = parseInt($(quantityInput).val()) || 1;
if (workId && quantity > 0) {
let fieldId = workSelect.attr('id');
checkStockAvailability(workId, quantity, function(stockData) {
displayStockWarning(fieldId, stockData);
// Store warning data for form submission validation
if (stockData && !stockData.available) {
stockWarnings[fieldId] = stockData;
} else {
delete stockWarnings[fieldId];
}
});
}
}
function logout(event){
event.preventDefault();
Swal.fire({
title: 'Logout?',
text: "Anda akan keluar dari sistem!",
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#dedede',
confirmButtonText: 'Logout'
}).then((result) => {
if (result.value) {
$('#logout-form').submit();
}
})
}
function addFormField(form) {
var id = $(`.${form}_field_counter`).val();
id = parseInt(id) + 1;
$(`
<div class="form-group row" id="${form}_field${id}">
<div class="col-6">
<select name="${form}_work_id[]" id="${form}_work${id}" class="form-control">
<option selected disabled>Pekerjaan</option>
</select>
</div>
<div class="col-4">
<input type="number" class="form-control" name="${form}_quantity[]" placeholder="Qty">
</div>
<div class="col-2">
<button class="btn btn-danger" onclick="removeFormField('${form}', ${id})" style="width: 100%; opacity: 1;" id="remove${id}">X</button>
</div>
</div>
`).insertBefore($(".btn-add-field-"+form).parent().parent());
let category_id = 1;
if(form == 'wash') {
category_id = 1;
}else if(form == 'work') {
category_id = 3;
}
let ajax_work_url = $("input[name='ajax_work_url']").val()
ajax_work_url = ajax_work_url.replace(':id', category_id);
getWork(ajax_work_url, form, id)
if(id > 1) {
$(".work-btn-remove").removeAttr('disabled')
$(".work-btn-remove").removeClass("disabled")
$(".work-btn-remove").css("opacity", "1")
}
$(`.${form}_field_counter`).val(id);
}
function removeFormField(form, id) {
var attr = $(".work-btn-remove").attr('disabled');
var id = $(`.${form}_field_counter`).val();
if (typeof attr !== 'undefined' && attr !== false) {
}else{
id = parseInt(id) - 1;
$(`.${form}_field_counter`).val(id);
$(`#${form}_field${id}`).remove();
}
if(id < 2) {
$(".work-btn-remove").attr("disabled", "disabled")
$(".work-btn-remove").addClass("disabled")
$(".work-btn-remove").css("opacity", "0.5")
}
}
function getWork(ajax_work_url, form, id) {
$.get(ajax_work_url, function(res) {
var $select = $(`#${form}_work${id}`);
// Clear existing options except the first one (placeholder)
$select.find('option:not(:first)').remove();
// Add new options
$.each(res.data, function (i, item) {
$select.append($('<option>', {
value: item.id,
text : item.name
}));
});
// Ensure placeholder is still properly set
var $placeholder = $select.find('option:first');
if ($placeholder.length) {
$placeholder.prop('disabled', true).prop('selected', true).val('');
}
})
}
// Add event listeners for existing fields
$(document).ready(function() {
// Initial fields (work1, work2, etc.)
$('select[name="work_id[]"]').on('change', function() {
handleWorkSelectionChange(this);
});
$('input[name="quantity[]"]').on('input', function() {
handleQuantityChange(this);
});
// Check stock for pre-filled fields
$('select[name="work_id[]"]').each(function() {
if ($(this).val()) {
handleWorkSelectionChange(this);
}
});
// Ensure placeholder options are properly disabled and not selectable
$('select[name="work_id[]"]').each(function() {
var $select = $(this);
var $placeholder = $select.find('option:first');
// Make sure placeholder is disabled and has empty value
if ($placeholder.length) {
$placeholder.prop('disabled', true).prop('selected', true).val('');
}
// Prevent auto-selection of first non-disabled option
$select.on('focus', function() {
if (!$(this).val()) {
$(this).find('option:first').prop('selected', true);
}
});
});
// Handle form errors - ensure work selections are maintained
@if($errors->any() && (old('form') == 'work' || old('form') == 'wash'))
// When there are form errors, ensure the correct work selections are maintained
// The old values are already handled by the Blade template in the HTML
// We just need to ensure proper placeholder handling
$('select[name="work_id[]"]').each(function() {
var $select = $(this);
// If no value is selected, ensure placeholder is selected
if (!$select.val() || $select.val() === '') {
var $placeholder = $select.find('option:first');
if ($placeholder.length) {
$placeholder.prop('selected', true);
}
} else {
// Trigger change event to update any dependent fields
$select.trigger('change');
}
});
@endif
});
// Override addFormField function to include event listeners
function addFormFieldWithStockCheck(form) {
addFormField(form); // Call original function
// Add event listeners to new field
setTimeout(function() {
var id = $(`.${form}_field_counter`).val();
var $newSelect = $(`#${form}_work${id}`);
// Ensure placeholder is properly set
var $placeholder = $newSelect.find('option:first');
if ($placeholder.length) {
$placeholder.prop('disabled', true).prop('selected', true).val('');
}
$newSelect.on('change', function() {
handleWorkSelectionChange(this);
});
$newSelect.closest('.form-group').find('input[name="quantity[]"]').on('input', function() {
handleQuantityChange(this);
});
// Prevent auto-selection of first non-disabled option
$newSelect.on('focus', function() {
if (!$(this).val()) {
$(this).find('option:first').prop('selected', true);
}
});
}, 100);
}
$("#workForm").submit(function(e) {
// Validate required fields
var spkNo = $('input[name="spk_no"]').val().trim();
var policeNumber = $('input[name="police_number"]').val().trim();
var userSaId = $('select[name="user_sa_id"]').val();
var date = $('input[name="date"]').val().trim();
var errorMessages = [];
if (!spkNo) {
errorMessages.push('No. SPK harus diisi');
$('input[name="spk_no"]').addClass('is-invalid');
} else {
$('input[name="spk_no"]').removeClass('is-invalid');
}
if (!policeNumber) {
errorMessages.push('No. Polisi harus diisi');
$('input[name="police_number"]').addClass('is-invalid');
} else {
$('input[name="police_number"]').removeClass('is-invalid');
}
if (!userSaId || userSaId === '') {
errorMessages.push('Service Advisor harus dipilih');
$('select[name="user_sa_id"]').addClass('is-invalid');
} else {
$('select[name="user_sa_id"]').removeClass('is-invalid');
}
if (!date) {
errorMessages.push('Tanggal Pekerjaan harus diisi');
$('input[name="date"]').addClass('is-invalid');
} else {
$('input[name="date"]').removeClass('is-invalid');
}
if (errorMessages.length > 0) {
e.preventDefault();
Swal.fire({
title: 'Validasi Gagal',
html: `
<div class="text-left">
<p class="mb-3">Mohon lengkapi field berikut:</p>
<ul class="text-left">
${errorMessages.map(msg => '<li>' + msg + '</li>').join('')}
</ul>
</div>
`,
icon: 'warning',
confirmButtonText: 'OK'
});
return false;
}
// Validate that at least one work is selected
var hasSelectedWork = false;
$('select[name="work_id[]"]').each(function() {
if ($(this).val() && $(this).val() !== '') {
hasSelectedWork = true;
return false; // break loop
}
});
if (!hasSelectedWork) {
e.preventDefault();
Swal.fire({
title: 'Peringatan',
text: 'Minimal pilih satu pekerjaan sebelum menyimpan!',
icon: 'warning',
confirmButtonText: 'OK'
});
return false;
}
// Check if there are stock warnings
if (Object.keys(stockWarnings).length > 0) {
e.preventDefault();
let warningMessages = [];
Object.values(stockWarnings).forEach(function(warning) {
warningMessages.push(warning.message);
warning.details.forEach(function(detail) {
if (!detail.is_available) {
warningMessages.push(`- ${detail.product_name}: Butuh ${detail.required_quantity}, Tersedia ${detail.available_stock}`);
}
});
});
Swal.fire({
title: 'Peringatan Stock Tidak Mencukupi',
html: `
<div class="text-left">
<p class="mb-3">Ada beberapa pekerjaan yang memerlukan produk dengan stock tidak mencukupi:</p>
<div class="alert alert-warning text-left">
${warningMessages.join('<br>')}
</div>
<p class="mb-0"><strong>Apakah Anda yakin ingin melanjutkan?</strong></p>
<small class="text-muted">Transaksi akan tetap dibuat, namun stock akan menjadi negatif.</small>
</div>
`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#ffc107',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Lanjutkan',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
$(".button-save").attr("disabled", true);
$(".button-save").addClass("disabled");
$("#workForm")[0].submit(); // Submit the form
}
});
return false;
}
$(".button-save").attr("disabled", true);
$(".button-save").addClass("disabled");
return true;
})
$("#washForm").submit(function(e) {
// Validate required fields
var spkNo = $('input[name="spk_no"]').val().trim();
var policeNumber = $('input[name="police_number"]').val().trim();
var userSaId = $('select[name="user_sa_id"]').val();
var date = $('input[name="date"]').val().trim();
var errorMessages = [];
if (!spkNo) {
errorMessages.push('No. SPK harus diisi');
$('input[name="spk_no"]').addClass('is-invalid');
} else {
$('input[name="spk_no"]').removeClass('is-invalid');
}
if (!policeNumber) {
errorMessages.push('No. Polisi harus diisi');
$('input[name="police_number"]').addClass('is-invalid');
} else {
$('input[name="police_number"]').removeClass('is-invalid');
}
if (!userSaId || userSaId === '') {
errorMessages.push('Service Advisor harus dipilih');
$('select[name="user_sa_id"]').addClass('is-invalid');
} else {
$('select[name="user_sa_id"]').removeClass('is-invalid');
}
if (!date) {
errorMessages.push('Tanggal Pekerjaan harus diisi');
$('input[name="date"]').addClass('is-invalid');
} else {
$('input[name="date"]').removeClass('is-invalid');
}
if (errorMessages.length > 0) {
e.preventDefault();
Swal.fire({
title: 'Validasi Gagal',
html: `
<div class="text-left">
<p class="mb-3">Mohon lengkapi field berikut:</p>
<ul class="text-left">
${errorMessages.map(msg => '<li>' + msg + '</li>').join('')}
</ul>
</div>
`,
icon: 'warning',
confirmButtonText: 'OK'
});
return false;
}
$(".button-save").attr("disabled", true);
$(".button-save").addClass("disabled");
return true;
})
$("#opnameForm").submit(function(e) {
e.preventDefault();
// Save current tab to localStorage
localStorage.setItem('activeTab', '#opname');
// Validate form
var hasValidStock = false;
var invalidRows = [];
$('.physical-stock').each(function(index) {
var value = $(this).val();
var row = $(this).closest('tr');
var productName = row.find('td:first').text().trim();
// Check if value is valid (including 0)
if (value !== '' && value !== null && value !== undefined) {
var numValue = parseFloat(value);
if (!isNaN(numValue) && numValue >= 0) {
hasValidStock = true;
// Ensure the value is properly formatted
$(this).val(numValue.toFixed(2));
} else {
invalidRows.push(productName + ' (nilai tidak valid)');
}
}
// Don't remove elements here - let them stay for re-editing
});
// Show error if no valid stock entries
if (!hasValidStock) {
resetSubmitButton();
highlightInvalidFields();
Swal.fire({
icon: 'warning',
title: 'Peringatan',
text: 'Minimal harus ada satu produk dengan stock fisik yang diisi dengan benar!'
});
return false;
}
// Show error if there are invalid entries
if (invalidRows.length > 0) {
resetSubmitButton();
highlightInvalidFields();
Swal.fire({
icon: 'warning',
title: 'Data Tidak Valid',
text: 'Perbaiki data berikut: ' + invalidRows.join(', ')
});
return false;
}
// Get opname date or use today as default
var opnameDate = $('#date-opname').val();
if (!opnameDate) {
// Set default to today if empty
var today = new Date().toISOString().split('T')[0];
$('#date-opname').val(today);
opnameDate = today;
}
// Validate date format (YYYY-MM-DD)
var datePattern = /^(\d{4})-(\d{2})-(\d{2})$/;
if (!datePattern.test(opnameDate)) {
resetSubmitButton();
Swal.fire({
icon: 'warning',
title: 'Format Tanggal Salah',
text: 'Format tanggal harus YYYY-MM-DD (contoh: 2023-12-25)'
});
return false;
}
// Validate if date is valid
var dateParts = opnameDate.match(datePattern);
var year = parseInt(dateParts[1], 10);
var month = parseInt(dateParts[2], 10);
var day = parseInt(dateParts[3], 10);
var testDate = new Date(year, month - 1, day);
if (testDate.getDate() !== day || testDate.getMonth() !== (month - 1) || testDate.getFullYear() !== year) {
resetSubmitButton();
Swal.fire({
icon: 'warning',
title: 'Tanggal Tidak Valid',
text: 'Tanggal yang dimasukkan tidak valid!'
});
return false;
}
// Check if date is not in the future
var today = new Date();
today.setHours(23, 59, 59, 999); // Set to end of today
if (testDate > today) {
resetSubmitButton();
Swal.fire({
icon: 'warning',
title: 'Tanggal Tidak Valid',
text: 'Tanggal opname tidak boleh lebih dari hari ini!'
});
return false;
}
$("#btn-save-opname").attr("disabled", true);
$("#btn-save-opname").addClass("disabled");
$("#btn-save-opname").html('<i class="fa fa-spinner fa-spin"></i> Menyimpan...');
// Date format is already YYYY-MM-DD, no conversion needed
// Clean up empty rows before submit (remove hidden inputs for truly empty fields only)
$('.physical-stock').each(function() {
var value = $(this).val();
var row = $(this).closest('tr');
// Only remove hidden inputs if physical stock is truly empty (not 0)
// Keep 0 values as they are valid input
if (value === '' || value === null || value === undefined) {
row.find('input[name="product_id[]"]').remove();
row.find('input[name="dealer_id_stock[]"]').remove();
row.find('input[name="system_stock[]"]').remove();
// But keep the physical_stock input so it can be re-edited if form fails
}
});
// Submit form
this.submit();
})
// Handle mutasi form submission
$(document).on('submit', '#mutasiForm', function(e) {
e.preventDefault();
// Save current tab to localStorage
localStorage.setItem('activeTab', '#mutasi');
// Validate form
var isValid = true;
var errorMessages = [];
// Check if dealer tujuan is selected
var dealerTujuan = $('#to_dealer_id').val();
console.log('Dealer tujuan value:', dealerTujuan);
if (!dealerTujuan || dealerTujuan === '') {
errorMessages.push('Pilih dealer tujuan');
isValid = false;
}
// Check if at least one product is selected with quantity
var hasProducts = false;
var productCount = 0;
$('.product-select-mutasi').each(function(index) {
var productId = $(this).val();
var quantity = $(this).closest('tr').find('.quantity-input-mutasi').val();
console.log(`Product ${index}:`, 'ID =', productId, 'Qty =', quantity);
if (productId && productId !== '' && quantity && parseFloat(quantity) > 0) {
hasProducts = true;
productCount++;
}
});
if (!hasProducts) {
errorMessages.push('Minimal pilih satu produk dengan quantity yang valid');
isValid = false;
}
// Additional validation: check all required fields
var requiredFields = [
{name: 'to_dealer_id', label: 'Dealer Tujuan'},
{name: 'from_dealer_id', label: 'Dealer Asal'}
];
requiredFields.forEach(function(field) {
var value = $(`[name="${field.name}"]`).val();
console.log(`${field.name} value:`, value);
if (!value || value === '') {
errorMessages.push(`${field.label} harus diisi`);
isValid = false;
}
});
// Check products array
var hasValidProduct = false;
$('select[name^="products["][name$="][product_id]"]').each(function(index) {
var productId = $(this).val();
var quantityName = $(this).attr('name').replace('[product_id]', '[quantity_requested]');
var quantity = $(`[name="${quantityName}"]`).val();
console.log(`Product validation ${index}:`, productId, quantity);
if (productId && productId !== '' && quantity && parseFloat(quantity) > 0) {
hasValidProduct = true;
}
});
if (!hasValidProduct) {
errorMessages.push('Minimal harus ada satu produk dengan quantity yang valid');
isValid = false;
}
console.log('Final validation result:', isValid);
console.log('Error messages:', errorMessages);
// Check for invalid quantities
var hasInvalidQuantity = false;
$('.quantity-input-mutasi').each(function() {
if ($(this).hasClass('is-invalid')) {
hasInvalidQuantity = true;
return false; // break loop
}
});
if (hasInvalidQuantity) {
errorMessages.push('Perbaiki quantity yang melebihi stock tersedia');
isValid = false;
}
if (!isValid) {
// Highlight invalid fields
if (!$('#to_dealer_id').val()) {
$('#to_dealer_id').addClass('is-invalid');
} else {
$('#to_dealer_id').removeClass('is-invalid');
}
// Check products
$('select[name^="products["][name$="][product_id]"]').each(function() {
var productId = $(this).val();
var quantityInput = $(this).closest('tr').find('.quantity-input-mutasi');
var quantity = quantityInput.val();
if (!productId || productId === '') {
$(this).addClass('is-invalid');
} else {
$(this).removeClass('is-invalid');
}
if (!quantity || parseFloat(quantity) <= 0) {
quantityInput.addClass('is-invalid');
} else {
quantityInput.removeClass('is-invalid');
}
});
if (typeof Swal !== 'undefined') {
Swal.fire({
type: 'warning',
title: 'Validasi Gagal',
text: errorMessages.join(', ')
});
} else {
alert('Validasi Gagal: ' + errorMessages.join(', '));
}
return false;
}
// Show confirmation
if (typeof Swal !== 'undefined') {
Swal.fire({
title: 'Kirim Mutasi?',
text: "Mutasi akan dikirim ke dealer tujuan",
type: 'question',
showCancelButton: true,
confirmButtonColor: '#ffc107',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Ya, Kirim',
cancelButtonText: 'Batal'
}).then((result) => {
console.log('SweetAlert result:', result);
if (result.value === true) { // For older SweetAlert2
console.log('Confirmed, submitting...');
submitMutasiFormDirect();
}
});
} else {
// Fallback to native confirm
if (confirm('Kirim Mutasi? Mutasi akan dikirim ke dealer tujuan')) {
submitMutasiFormDirect();
}
}
})
// Initialize datepickers when document is ready
$(document).ready(function() {
$("#date-work").datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
todayHighlight: true
});
$("#date-wash").datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
todayHighlight: true
});
$("#date-opname").datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
todayHighlight: true,
startDate: '-30d',
endDate: '+0d'
});
$("#date-mutasi").datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
todayHighlight: true
});
});
// Calculate difference for opname
$(document).on('input change keyup', '.physical-stock', function() {
var systemStock = parseFloat($(this).data('system')) || 0;
var inputValue = $(this).val();
// Handle empty input - set to 0 for calculation
var physicalStock = 0;
if (inputValue !== '' && inputValue !== null && inputValue !== undefined) {
physicalStock = parseFloat(inputValue) || 0;
}
var difference = physicalStock - systemStock;
var differenceCell = $(this).closest('tr').find('.difference');
differenceCell.text(difference.toFixed(2));
// Add color coding for difference
if (difference > 0) {
differenceCell.removeClass('text-danger').addClass('text-success');
} else if (difference < 0) {
differenceCell.removeClass('text-success').addClass('text-danger');
} else {
differenceCell.removeClass('text-success text-danger');
}
// Update product counter
updateProductCounter();
});
// Function to update product counter
function updateProductCounter() {
var filledProducts = 0;
var totalProducts = $('.physical-stock').length;
$('.physical-stock').each(function() {
var value = $(this).val();
// Count as filled if it's a valid number (including 0)
if (value !== '' && value !== null && value !== undefined && !isNaN(parseFloat(value)) && parseFloat(value) >= 0) {
filledProducts++;
}
});
// Update button text to show progress
var buttonText = 'Simpan Opname';
if (filledProducts > 0) {
buttonText += ` (${filledProducts}/${totalProducts} produk)`;
}
if (!$('#btn-save-opname').hasClass('disabled')) {
$('#btn-save-opname').html(buttonText);
}
}
// Handle when input loses focus - don't auto-fill, let user decide
$(document).on('blur', '.physical-stock', function() {
// Trigger calculation even for empty values
$(this).trigger('input');
});
// Function to reset button state if validation fails
function resetSubmitButton() {
$("#btn-save-opname").attr("disabled", false);
$("#btn-save-opname").removeClass("disabled");
updateProductCounter(); // Update with current counter
}
// Function to show field error highlighting
function highlightInvalidFields() {
$('.physical-stock').each(function() {
var value = $(this).val();
var $input = $(this);
if (value !== '' && value !== null && value !== undefined) {
var numValue = parseFloat(value);
if (isNaN(numValue) || numValue < 0) {
$input.addClass('is-invalid');
} else {
$input.removeClass('is-invalid');
}
} else {
$input.removeClass('is-invalid');
}
});
}
// Remove error styling when user starts typing
$(document).on('input', '.physical-stock', function() {
$(this).removeClass('is-invalid');
});
// Remove invalid styling when user starts typing in required fields
$(document).on('input', 'input[name="spk_no"], input[name="police_number"], input[name="date"]', function() {
$(this).removeClass('is-invalid');
});
$(document).on('change', 'select[name="user_sa_id"]', function() {
$(this).removeClass('is-invalid');
});
// Handle server-side errors - scroll to first error and highlight
$(document).ready(function() {
// Set default date for opname if empty
if ($('#date-opname').val() === '') {
var today = new Date().toISOString().split('T')[0];
$('#date-opname').val(today);
}
// Initialize mutasi form
updateRemoveButtonsMutasi();
// Initialize select2 for mutasi form
initMutasiSelect2();
// Check if we should show specific tab (after form submission)
@if(session('success') || session('error') || $errors->any())
@if(session('active_tab') == 'opname')
// Activate stock tab and opname sub-tab
$('.nav-link[href="#stock"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#opname"]').tab('show');
}, 100);
@elseif(session('active_tab') == 'mutasi')
// Activate stock tab and mutasi sub-tab
$('.nav-link[href="#stock"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#mutasi"]').tab('show');
}, 100);
@elseif(session('active_tab') == 'penerimaan')
// Activate stock tab and penerimaan sub-tab
$('.nav-link[href="#stock"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#penerimaan"]').tab('show');
// Initialize table after tab is shown
setTimeout(function() {
initReceiveMutationsTable();
}, 200);
}, 100);
@elseif($errors->any() && old('form') == 'work')
// Activate transaksi tab and form kerja sub-tab when there are work form errors
$('.nav-link[href="#transaksi"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#form-kerja"]').tab('show');
}, 100);
@elseif($errors->any() && old('form') == 'wash')
// Activate transaksi tab and form cuci sub-tab when there are wash form errors
$('.nav-link[href="#transaksi"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#form-cuci"]').tab('show');
}, 100);
@endif
@endif
// Check if there are validation errors
if ($('.is-invalid').length > 0) {
// Scroll to first error
$('html, body').animate({
scrollTop: $('.is-invalid:first').offset().top - 100
}, 500);
// Show alert for validation errors
@if(session('error'))
Swal.fire({
type: 'error',
title: 'Terjadi Kesalahan',
text: '{{ session("error") }}',
confirmButtonText: 'OK'
});
@endif
}
// Handle success/error messages for both opname and mutasi
@if(session('success'))
Swal.fire({
type: 'success',
title: 'Berhasil!',
text: '{{ session("success") }}',
timer: 3000,
showConfirmButton: false
}).then(() => {
// Check if this is from mutasi form (contains "Mutasi" in message)
var successMessage = '{{ session("success") }}';
var activeTab = '{{ session("active_tab") }}';
if (successMessage.toLowerCase().includes('mutasi') && (successMessage.toLowerCase().includes('berhasil dibuat') || activeTab === 'mutasi')) {
// Reset mutasi form after success for CREATING mutations
$('#mutasiForm')[0].reset();
$('#products-tbody-mutasi').html(`
<tr class="product-row-mutasi" data-index="0">
<td>
<select name="products[0][product_id]" class="form-control product-select-mutasi" required>
<option value="">Pilih Produk</option>
@foreach($products as $product)
<option value="{{ $product->id }}">{{ $product->name }}</option>
@endforeach
</select>
</td>
<td class="text-center">
<span class="available-stock-mutasi text-muted">-</span>
</td>
<td>
<input type="number"
name="products[0][quantity_requested]"
class="form-control quantity-input-mutasi"
min="0.01"
step="0.01"
placeholder="0"
required>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-product-mutasi" disabled>
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
`);
productIndexMutasi = 0;
updateRemoveButtonsMutasi();
// Reinitialize select2
setTimeout(function() {
initMutasiSelect2();
}, 100);
$("#btn-save-mutasi").attr("disabled", false);
$("#btn-save-mutasi").removeClass("disabled");
$("#btn-save-mutasi").html('Kirim Mutasi');
} else if (successMessage.toLowerCase().includes('penerimaan') || (successMessage.toLowerCase().includes('mutasi') && successMessage.toLowerCase().includes('diterima')) || activeTab === 'penerimaan') {
// For penerimaan mutasi, just refresh the table without resetting form
// Activate the correct tab first
$('.nav-link[href="#stock"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#penerimaan"]').tab('show');
// Stay on penerimaan tab and refresh table
setTimeout(function() {
if (receiveMutationsTable && $.fn.DataTable.isDataTable('#receiveMutationsTable')) {
receiveMutationsTable.ajax.reload(null, false); // Don't reset paging
} else {
// Initialize table if not already initialized
initReceiveMutationsTable();
}
}, 300);
}, 100);
// Close any open modals
$('#mutationDetailModal').modal('hide');
} else if (successMessage.toLowerCase().includes('disetujui') || successMessage.toLowerCase().includes('ditolak') || activeTab === 'persetujuan') {
// For approval/rejection, refresh the table
$('.nav-link[href="#stock"]').tab('show');
setTimeout(function() {
$('.nav-link[href="#penerimaan"]').tab('show');
// Refresh table to show updated status
setTimeout(function() {
if (receiveMutationsTable && $.fn.DataTable.isDataTable('#receiveMutationsTable')) {
receiveMutationsTable.ajax.reload(null, false);
} else {
initReceiveMutationsTable();
}
}, 300);
}, 100);
} else if (successMessage.toLowerCase().includes('opname') || activeTab === 'opname') {
// Reset opname form after success
$('#opnameForm')[0].reset();
$('.physical-stock').val('').removeClass('is-invalid');
$('.difference').text('0.00').removeClass('text-success text-danger');
$("#btn-save-opname").attr("disabled", false);
$("#btn-save-opname").removeClass("disabled");
$("#btn-save-opname").html('Simpan Opname');
// Set default date
var today = new Date().toISOString().split('T')[0];
$('#date-opname').val(today);
// Update product counter
updateProductCounter();
}
});
@endif
@if(session('error'))
Swal.fire({
type: 'error',
title: 'Terjadi Kesalahan',
text: '{{ session("error") }}',
confirmButtonText: 'OK'
}).then(() => {
// Check if this is from opname form error
var activeTab = '{{ session("active_tab") }}';
if (activeTab === 'opname') {
// Reset button states for opname form
$("#btn-save-opname").attr("disabled", false);
$("#btn-save-opname").removeClass("disabled");
$("#btn-save-opname").html('Simpan Opname');
} else if (activeTab === 'mutasi') {
// Reset button states for mutasi form
$("#btn-save-mutasi").attr("disabled", false);
$("#btn-save-mutasi").removeClass("disabled");
$("#btn-save-mutasi").html('Kirim Mutasi');
}
});
@endif
// Handle validation errors
@if($errors->any())
var errorMessages = [];
@foreach($errors->all() as $error)
errorMessages.push('{{ $error }}');
@endforeach
Swal.fire({
type: 'error',
title: 'Validasi Gagal',
html: errorMessages.join('<br>'),
confirmButtonText: 'OK'
});
@endif
});
// Handle opname date field - set default if becomes empty
$('#date-opname').on('blur', function() {
if ($(this).val() === '') {
var today = new Date().toISOString().split('T')[0];
$(this).val(today);
}
});
// Handle mutasi form - similar to create mutation
var productIndexMutasi = 0;
// Function to submit mutasi form directly
function submitMutasiFormDirect() {
console.log('=== SUBMITTING FORM ===');
// Set loading state
$("#btn-save-mutasi").attr("disabled", true);
$("#btn-save-mutasi").addClass("disabled");
$("#btn-save-mutasi").html('<i class="fa fa-spinner fa-spin"></i> Mengirim...');
// Get form element directly
var form = document.getElementById('mutasiForm');
if (!form) {
console.error('Form not found!');
alert('Form tidak ditemukan!');
return;
}
console.log('Form found:', form);
console.log('Form action:', form.action);
console.log('Form method:', form.method);
// Check form data
var formData = new FormData(form);
console.log('Form data:');
for (var pair of formData.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
// Submit form directly - simplified approach
try {
console.log('Attempting form.submit()...');
// Temporarily remove the submit event listener to prevent recursion
$(form).off('submit');
// Submit the form
form.submit();
console.log('form.submit() called successfully');
} catch (error) {
console.error('Form submission error:', error);
// Reset button if submission fails
$("#btn-save-mutasi").attr("disabled", false);
$("#btn-save-mutasi").removeClass("disabled");
$("#btn-save-mutasi").html('Kirim Mutasi');
alert('Terjadi kesalahan saat mengirim form. Silakan coba lagi.');
}
}
// Add product row for mutasi
$('#add-product-mutasi').click(function() {
productIndexMutasi++;
var newRow = `
<tr class="product-row-mutasi" data-index="${productIndexMutasi}">
<td>
<select name="products[${productIndexMutasi}][product_id]" class="form-control product-select-mutasi" required>
<option value="">Pilih Produk</option>
@foreach($products as $product)
<option value="{{ $product->id }}">{{ $product->name }}</option>
@endforeach
</select>
</td>
<td class="text-center">
<span class="available-stock-mutasi text-muted">-</span>
</td>
<td>
<input type="number"
name="products[${productIndexMutasi}][quantity_requested]"
class="form-control quantity-input-mutasi"
min="0.01"
step="0.01"
placeholder="0"
required>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-product-mutasi">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
`;
$('#products-tbody-mutasi').append(newRow);
// Initialize select2 for the new product select
var newSelect = $(`select[name="products[${productIndexMutasi}][product_id]"]`);
newSelect.select2({
placeholder: 'Pilih Produk',
width: '100%',
language: {
noResults: function() {
return "Tidak ada hasil ditemukan";
},
searching: function() {
return "Mencari...";
}
}
});
updateRemoveButtonsMutasi();
});
// Remove product row for mutasi
$(document).on('click', '.remove-product-mutasi', function() {
$(this).closest('tr').remove();
updateRemoveButtonsMutasi();
});
// Update remove buttons state for mutasi
function updateRemoveButtonsMutasi() {
var rows = $('.product-row-mutasi');
if (rows.length <= 1) {
$('.remove-product-mutasi').prop('disabled', true);
} else {
$('.remove-product-mutasi').prop('disabled', false);
}
}
// Handle product selection change for mutasi - get stock info
$(document).on('change', '.product-select-mutasi', function() {
var productId = $(this).val();
var dealerId = {{ $mechanic->dealer_id }};
var stockSpan = $(this).closest('tr').find('.available-stock-mutasi');
if (productId) {
// Get stock info via AJAX
$.ajax({
url: '{{ route("mutations.get-product-stock") }}',
method: 'GET',
data: {
product_id: productId,
dealer_id: dealerId
},
success: function(response) {
stockSpan.text(parseFloat(response.current_stock).toFixed(2));
stockSpan.removeClass('text-muted').addClass('text-info');
},
error: function() {
stockSpan.text('-');
stockSpan.removeClass('text-info').addClass('text-muted');
}
});
} else {
stockSpan.text('-');
stockSpan.removeClass('text-info').addClass('text-muted');
}
});
// Initialize select2 when mutasi tab is shown
$('a[href="#mutasi"]').on('shown.bs.tab', function (e) {
setTimeout(function() {
initMutasiSelect2();
}, 100);
});
// Validate quantity against available stock for mutasi
$(document).on('input', '.quantity-input-mutasi', function() {
var quantity = parseFloat($(this).val()) || 0;
var stockText = $(this).closest('tr').find('.available-stock-mutasi').text();
var availableStock = parseFloat(stockText) || 0;
if (quantity > availableStock && availableStock > 0) {
$(this).addClass('is-invalid');
if (!$(this).siblings('.invalid-feedback').length) {
$(this).after('<div class="invalid-feedback">Quantity melebihi stock tersedia</div>');
}
} else {
$(this).removeClass('is-invalid');
$(this).siblings('.invalid-feedback').remove();
}
});
// Remove error styling when user starts filling fields
$(document).on('change', '#to_dealer_id', function() {
$(this).removeClass('is-invalid');
});
$(document).on('change', '.product-select-mutasi', function() {
$(this).removeClass('is-invalid');
});
$(document).on('input', '.quantity-input-mutasi', function() {
if (parseFloat($(this).val()) > 0) {
$(this).removeClass('is-invalid');
}
});
// Initialize Select2 for mutasi form
function initMutasiSelect2() {
// Initialize select2 for dealer tujuan
if ($('#to_dealer_id').length && !$('#to_dealer_id').hasClass('select2-hidden-accessible')) {
$('#to_dealer_id').select2({
placeholder: 'Pilih Dealer Tujuan',
width: '100%',
language: {
noResults: function() {
return "Tidak ada hasil ditemukan";
},
searching: function() {
return "Mencari...";
}
}
});
}
// Initialize select2 for all product selects
$('.product-select-mutasi').each(function() {
if (!$(this).hasClass('select2-hidden-accessible')) {
$(this).select2({
placeholder: 'Pilih Produk',
width: '100%',
language: {
noResults: function() {
return "Tidak ada hasil ditemukan";
},
searching: function() {
return "Mencari...";
}
}
});
}
});
}
// Initialize DataTable for receive mutations
var receiveMutationsTable;
function initReceiveMutationsTable() {
if (receiveMutationsTable) {
receiveMutationsTable.destroy();
}
receiveMutationsTable = $('#receiveMutationsTable').DataTable({
processing: true,
serverSide: true,
ajax: {
url: '{{ route("mutations.get-pending-mutations") }}',
data: {
dealer_id: {{ $mechanic->dealer_id }}
}
},
columns: [
{data: 'mutation_number', name: 'mutation_number'},
{data: 'from_dealer', name: 'from_dealer'},
{data: 'to_dealer', name: 'to_dealer'},
{data: 'status', name: 'status', orderable: false},
{data: 'total_items', name: 'total_items'},
{data: 'created_at', name: 'created_at'},
{data: 'action', name: 'action', orderable: false, searchable: false}
],
pageLength: 10,
responsive: true,
scrollX: true
});
}
// Function removed since we use single table for both receive and approval mutations
// Show mutation detail modal
function showMutationDetail(mutationId) {
$('#mutationDetailModal').modal('show');
$('#mutationDetailContent').html(`
<div class="text-center">
<i class="fa fa-spinner fa-spin fa-2x"></i>
<p class="mt-2">Memuat data...</p>
</div>
`);
$('#receiveButton').hide();
// Load mutation detail via AJAX
$.ajax({
url: '{{ route("mutations.get-detail", ":id") }}'.replace(':id', mutationId),
method: 'GET',
success: function(response) {
if (response.success) {
renderMutationDetail(response.data);
} else {
$('#mutationDetailContent').html(`
<div class="alert alert-danger">
<i class="fa fa-exclamation-triangle"></i>
${response.message || 'Gagal memuat detail mutasi'}
</div>
`);
}
},
error: function(xhr) {
$('#mutationDetailContent').html(`
<div class="alert alert-danger">
<i class="fa fa-exclamation-triangle"></i>
Terjadi kesalahan saat memuat data
</div>
`);
}
});
}
// Render mutation detail in modal
function renderMutationDetail(mutation) {
var statusColor = mutation.status_color;
var statusLabel = mutation.status_label;
var isReceived = mutation.status.value === 'received';
var canBeReceived = mutation.can_be_received;
// Set form action URL
$('#receiveMutationForm').attr('action', '{{ route("mutations.receive", ":id") }}'.replace(':id', mutation.id));
// Build detail HTML
var detailHtml = `
<div class="row mb-3">
<div class="col-md-6">
<strong>No. Mutasi:</strong><br>
${mutation.mutation_number}
</div>
<div class="col-md-6">
<strong>Status:</strong><br>
<span class="font-weight-bold text-${statusColor}">${statusLabel}</span>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<strong>Dealer Asal:</strong><br>
${mutation.from_dealer.name}
</div>
<div class="col-md-6">
<strong>Tanggal:</strong><br>
${mutation.created_at_formatted}
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<strong>Dikirim oleh:</strong><br>
${mutation.requested_by ? mutation.requested_by.name : '-'}
</div>
</div>
${mutation.shipping_notes ? `
<div class="row mb-3">
<div class="col-md-12">
<strong>Catatan dari Pengirim:</strong><br>
<div class="alert alert-info mb-0">
<i class="fa fa-info-circle mr-2"></i>
${mutation.shipping_notes}
</div>
</div>
</div>
` : ''}
<hr>
<h6 class="mb-3">Detail Produk & Penerimaan:</h6>
<div class="table-responsive">
<table class="table table-bordered mutation-detail-table">
<thead>
<tr>
<th width="30%">Produk</th>
<th width="15%" class="text-center">Qty Diminta</th>
<th width="20%" class="text-center">Qty Disetujui</th>
<th width="35%">Catatan Produk</th>
</tr>
</thead>
<tbody>
`;
// Add product details with form inputs or read-only display
if (mutation.mutation_details && mutation.mutation_details.length > 0) {
mutation.mutation_details.forEach(function(detail) {
var quantityValue = detail.quantity_approved || detail.quantity_requested;
var notesValue = detail.notes || '';
detailHtml += `
<tr>
<td>${detail.product.name}</td>
<td class="text-center">
<span class="font-weight-bold text-info">${parseFloat(detail.quantity_requested).toFixed(2)}</span>
</td>
<td>
`;
if (canBeReceived && !isReceived) {
// Editable input for mutations that can be received
detailHtml += `
<input type="number"
name="products[${detail.id}][quantity_approved]"
class="form-control quantity-approved-input"
min="0"
max="${detail.quantity_requested}"
step="0.01"
value="${detail.quantity_requested}"
data-max="${detail.quantity_requested}"
placeholder="0.00">
`;
} else {
// Read-only display for received mutations
detailHtml += `
<span class="font-weight-bold ${quantityValue == detail.quantity_requested ? 'text-success' : 'text-warning'}">
${parseFloat(quantityValue).toFixed(2)}
</span>
${quantityValue != detail.quantity_requested ?
'<small class="text-muted d-block">(Sebagian)</small>' : ''}
`;
}
detailHtml += `
</td>
<td>
`;
if (canBeReceived && !isReceived) {
// Editable textarea for mutations that can be received
detailHtml += `
<textarea name="products[${detail.id}][notes]"
class="form-control"
rows="2"
placeholder="Catatan untuk produk ini...">${notesValue}</textarea>
`;
} else {
// Read-only display for received mutations
detailHtml += `
<span class="text-muted">${notesValue || '-'}</span>
`;
}
detailHtml += `
</td>
</tr>
`;
});
} else {
detailHtml += `
<tr>
<td colspan="4" class="text-center">Tidak ada detail produk</td>
</tr>
`;
}
detailHtml += `
</tbody>
</table>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label for="mutationNotes"><strong>Catatan Penerimaan:</strong></label>
`;
if (canBeReceived && !isReceived) {
// Editable textarea for mutations that can be received
detailHtml += `
<textarea name="reception_notes" id="mutationNotes" class="form-control" rows="3"
placeholder="Masukkan catatan kondisi barang saat diterima (opsional)...">${mutation.reception_notes || ''}</textarea>
`;
} else {
// Read-only display for received mutations
detailHtml += `
<div class="form-control-plaintext border rounded p-2 bg-light" style="min-height: 60px;">
${mutation.reception_notes || '<em class="text-muted">Tidak ada catatan</em>'}
</div>
`;
}
detailHtml += `
</div>
</div>
`;
// Add instructions based on status
if (canBeReceived && !isReceived) {
detailHtml += `
<div class="alert alert-info mb-0">
<div class="d-flex align-items-start">
<i class="fa fa-info-circle mt-1 mr-2"></i>
<div>
<strong>Instruksi Penerimaan:</strong>
<ul class="mb-0 mt-2">
<li>Periksa kondisi fisik produk yang diterima</li>
<li>Masukkan quantity yang benar-benar diterima untuk setiap produk</li>
<li>Quantity yang disetujui tidak boleh melebihi quantity yang diminta</li>
<li>Berikan catatan jika ada produk yang rusak atau tidak sesuai</li>
</ul>
<div class="mt-2 p-2 rounded">
<small><strong>Perhatian:</strong> Setelah diterima, catatan tidak dapat diubah lagi. Pastikan informasi sudah benar sebelum menerima mutasi.</small>
</div>
</div>
</div>
</div>
`;
} else if (isReceived) {
detailHtml += `
<div class="alert alert-success mb-0">
<div class="d-flex align-items-center">
<i class="fa fa-check-circle mr-2"></i>
<div>
<strong>Status:</strong> Mutasi telah diterima dan menunggu persetujuan admin.
<br><small>Stock akan dipindahkan setelah admin menyetujui mutasi ini.</small>
</div>
</div>
</div>
`;
}
$('#mutationDetailContent').html(detailHtml);
// Show receive button only if mutation can be received
if (canBeReceived && !isReceived) {
$('#receiveButton').show();
} else {
$('#receiveButton').hide();
}
// Add validation for quantity inputs (only for editable fields)
$('.quantity-approved-input').on('input', function() {
var value = parseFloat($(this).val()) || 0;
var max = parseFloat($(this).data('max')) || 0;
if (value > max) {
$(this).addClass('is-invalid');
if (!$(this).siblings('.invalid-feedback').length) {
$(this).after('<div class="invalid-feedback">Quantity tidak boleh melebihi quantity yang diminta</div>');
}
} else {
$(this).removeClass('is-invalid');
$(this).siblings('.invalid-feedback').remove();
}
});
}
// Handle receive form submission
$('#receiveMutationForm').on('submit', function(e) {
e.preventDefault();
// Validate form
var hasInvalidInput = $('.quantity-approved-input.is-invalid').length > 0;
if (hasInvalidInput) {
Swal.fire({
type: 'error',
title: 'Validasi Gagal',
text: 'Perbaiki quantity yang tidak valid sebelum melanjutkan'
});
return false;
}
// Check if at least one product has quantity approved > 0
var hasApprovedQuantity = false;
$('.quantity-approved-input').each(function() {
if (parseFloat($(this).val()) > 0) {
hasApprovedQuantity = true;
return false; // break loop
}
});
if (!hasApprovedQuantity) {
Swal.fire({
type: 'warning',
title: 'Peringatan',
text: 'Minimal satu produk harus memiliki quantity yang disetujui'
});
return false;
}
if (typeof Swal !== 'undefined') {
Swal.fire({
title: 'Konfirmasi Penerimaan Mutasi',
html: `
<div class="text-left">
<p class="mb-3">Dengan menerima mutasi ini:</p>
<ul class="text-muted mb-3" style="font-size: 14px;">
<li>Anda mengkonfirmasi telah menerima produk dari dealer pengirim</li>
<li>Data quantity dan catatan yang dimasukkan akan disimpan</li>
<li>Mutasi akan masuk ke tahap persetujuan untuk memindahkan stock</li>
<li>Catatan dan data penerimaan tidak dapat diubah setelah diterima</li>
</ul>
<div class="alert alert-info">
<i class="fa fa-info-circle mr-2"></i>
<small><strong>Info:</strong> Pastikan semua data sudah benar sebelum menerima mutasi.</small>
</div>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#28a745',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Ya, Terima Mutasi',
cancelButtonText: 'Batal',
width: '500px'
}).then((result) => {
if (result.isConfirmed) {
// Set loading state
$('#receiveButton').prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Memproses...');
// Submit form
this.submit();
}
});
} else {
if (confirm('Terima mutasi dengan data yang telah dimasukkan?')) {
$('#receiveButton').prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Memproses...');
this.submit();
}
}
});
// Initialize table when tab is shown
$('a[href="#penerimaan"]').on('shown.bs.tab', function (e) {
setTimeout(function() {
initReceiveMutationsTable();
}, 100);
});
// Note: Approve and reject buttons removed from transaction page
// These actions are now only available to admin users in the admin panel
// Save active tab to localStorage
$('.nav-link').on('click', function() {
var target = $(this).attr('href');
if (target) {
localStorage.setItem('activeTab', target);
}
});
// Restore active tab from localStorage if no server-side active tab
$(document).ready(function() {
var serverActiveTab = '{{ session("active_tab") }}';
// Only restore from localStorage if no server-side active tab
if (!serverActiveTab) {
var savedTab = localStorage.getItem('activeTab');
if (savedTab) {
// Activate main tab first if it's a sub-tab
if (savedTab.includes('opname') || savedTab.includes('mutasi') || savedTab.includes('penerimaan')) {
$('.nav-link[href="#stock"]').tab('show');
setTimeout(function() {
$('.nav-link[href="' + savedTab + '"]').tab('show');
}, 100);
} else {
$('.nav-link[href="' + savedTab + '"]').tab('show');
}
}
}
});
function createTransaction(form) {
let work_ids;
if(form == 'work') {
work_ids = $(`select[name='${form}_work_id[]']`).map(function (idx, elem) {
return $(elem).val();
}).get();
}
if(form == 'wash') {
work_ids = $(`input[name='${form}_work_id[]']`).map(function (idx, elem) {
return $(elem).val();
}).get();
}
let quantities = $(`input[name='${form}_quantity[]']`).map(function (idx, elem) {
return $(elem).val();
}).get();
let datas = [];
for(let i = 0; i < work_ids.length; i++) {
const data = {
user_id : $("input[name='mechanic_id']").val(),
dealer_id : $("input[name='dealer_id']").val(),
form : form,
work_id : work_ids[i],
qty : quantities[i],
spk : $(`input[name='${form}_spk_no']`).val(),
police_number : $(`input[name='${form}_police_number']`).val(),
qtys : $(`input[name='${form}_quantity']`).val(),
warranty : $(`select[name='${form}_warranty']`).val(),
user_sa_id : $(`select[name='${form}_user_sa_id']`).val(),
date : $(`input[name='${form}_date']`).val(),
}
datas.push(data)
}
$.ajax({
url: $(`#${form}Form`).attr("action"),
type: "POST",
data: {
data : datas
},
success: function(res) {
document.location.reload()
}
})
}
</script>
@endsection