|
Lines 198-203
Link Here
|
| 198 |
getAuthValueDropbox( 'sort2', sort2_authcat, destination_sort2, sort2 ); |
198 |
getAuthValueDropbox( 'sort2', sort2_authcat, destination_sort2, sort2 ); |
| 199 |
}); |
199 |
}); |
| 200 |
$("#budget_id").change(); |
200 |
$("#budget_id").change(); |
|
|
201 |
|
| 202 |
// Servicing Instructions Management |
| 203 |
initServicingInstructions(); |
| 201 |
}); |
204 |
}); |
| 202 |
|
205 |
|
| 203 |
function add_user(borrowernumber, borrowername) { |
206 |
function add_user(borrowernumber, borrowername) { |
|
Lines 225-230
Link Here
|
| 225 |
ids.splice(ids.indexOf(borrowernumber.toString()), 1); |
228 |
ids.splice(ids.indexOf(borrowernumber.toString()), 1); |
| 226 |
$("#users_ids").val(ids.join(':')); |
229 |
$("#users_ids").val(ids.join(':')); |
| 227 |
} |
230 |
} |
|
|
231 |
|
| 232 |
// Servicing Instructions Management |
| 233 |
var servicingInstructionGroups = []; |
| 234 |
var servicingInstructionAVs = [ |
| 235 |
[% FOREACH av IN servicing_instruction_authorised_values %] |
| 236 |
{ value: '[% av.authorised_value | html %]', label: '[% av.lib | html %]' }, |
| 237 |
[% END %] |
| 238 |
]; |
| 239 |
|
| 240 |
function initServicingInstructions() { |
| 241 |
// Load existing data from template |
| 242 |
try { |
| 243 |
var existingData = JSON.parse('[% servicing_instruction_groups_json | $raw %]'); |
| 244 |
servicingInstructionGroups = Array.isArray(existingData) ? existingData : []; |
| 245 |
} catch (e) { |
| 246 |
console.error('Failed to parse servicing instruction groups:', e); |
| 247 |
servicingInstructionGroups = []; |
| 248 |
} |
| 249 |
|
| 250 |
renderServicingInstructions(); |
| 251 |
updateJSON(); |
| 252 |
|
| 253 |
// Add group button handler |
| 254 |
$('#add_servicing_group').on('click', function() { |
| 255 |
addServicingGroup(); |
| 256 |
}); |
| 257 |
|
| 258 |
// Form submission handler - update JSON before submit |
| 259 |
$('form').on('submit', function() { |
| 260 |
updateJSON(); |
| 261 |
}); |
| 262 |
} |
| 263 |
|
| 264 |
function addServicingGroup() { |
| 265 |
servicingInstructionGroups.push([]); |
| 266 |
renderServicingInstructions(); |
| 267 |
updateJSON(); |
| 268 |
} |
| 269 |
|
| 270 |
function removeServicingGroup(groupIndex) { |
| 271 |
servicingInstructionGroups.splice(groupIndex, 1); |
| 272 |
renderServicingInstructions(); |
| 273 |
updateJSON(); |
| 274 |
} |
| 275 |
|
| 276 |
function addInstructionToGroup(groupIndex, type, value) { |
| 277 |
var group = servicingInstructionGroups[groupIndex]; |
| 278 |
|
| 279 |
// Validate: max 2 instructions per group |
| 280 |
if (group.length >= 2) { |
| 281 |
alert('Each service group can contain at most 2 instructions (one LVC and/or one LVT).'); |
| 282 |
return; |
| 283 |
} |
| 284 |
|
| 285 |
if (!type) type = 'LVC'; // Default to LVC (coded) |
| 286 |
if (!value) value = ''; |
| 287 |
|
| 288 |
// Check if this type already exists in the group |
| 289 |
var hasLVC = group.some(function(inst) { return inst.type === 'LVC'; }); |
| 290 |
var hasLVT = group.some(function(inst) { return inst.type === 'LVT'; }); |
| 291 |
|
| 292 |
if (type === 'LVC' && hasLVC) { |
| 293 |
alert('This group already has an LVC instruction. Each group can have only one LVC.'); |
| 294 |
return; |
| 295 |
} |
| 296 |
if (type === 'LVT' && hasLVT) { |
| 297 |
alert('This group already has an LVT instruction. Each group can have only one LVT.'); |
| 298 |
return; |
| 299 |
} |
| 300 |
|
| 301 |
servicingInstructionGroups[groupIndex].push({ type: type, value: value }); |
| 302 |
renderServicingInstructions(); |
| 303 |
updateJSON(); |
| 304 |
} |
| 305 |
|
| 306 |
function removeInstructionFromGroup(groupIndex, instructionIndex) { |
| 307 |
servicingInstructionGroups[groupIndex].splice(instructionIndex, 1); |
| 308 |
renderServicingInstructions(); |
| 309 |
updateJSON(); |
| 310 |
} |
| 311 |
|
| 312 |
function updateInstruction(groupIndex, instructionIndex, type, value) { |
| 313 |
var group = servicingInstructionGroups[groupIndex]; |
| 314 |
|
| 315 |
// Check if changing type would create a duplicate |
| 316 |
for (var i = 0; i < group.length; i++) { |
| 317 |
if (i !== instructionIndex && group[i].type === type) { |
| 318 |
alert('This group already has a ' + type + ' instruction. Each group can have only one of each type.'); |
| 319 |
renderServicingInstructions(); // Re-render to reset the select |
| 320 |
return; |
| 321 |
} |
| 322 |
} |
| 323 |
|
| 324 |
servicingInstructionGroups[groupIndex][instructionIndex] = { type: type, value: value }; |
| 325 |
updateJSON(); |
| 326 |
} |
| 327 |
|
| 328 |
function renderServicingInstructions() { |
| 329 |
var container = $('#servicing_instructions_container'); |
| 330 |
container.empty(); |
| 331 |
|
| 332 |
if (servicingInstructionGroups.length === 0) { |
| 333 |
container.append('<div class="hint">No servicing instructions added. Click "Add servicing group" to add one.</div>'); |
| 334 |
return; |
| 335 |
} |
| 336 |
|
| 337 |
servicingInstructionGroups.forEach(function(group, groupIndex) { |
| 338 |
var groupDiv = $('<div class="servicing_group"></div>'); |
| 339 |
groupDiv.css({ |
| 340 |
'border': '1px solid #ddd', |
| 341 |
'padding': '10px', |
| 342 |
'margin-bottom': '10px', |
| 343 |
'background': '#f9f9f9', |
| 344 |
'border-radius': '4px' |
| 345 |
}); |
| 346 |
|
| 347 |
var groupHeader = $('<div style="margin-bottom: 10px;"></div>'); |
| 348 |
groupHeader.append('<strong>Service Group ' + (groupIndex + 1) + '</strong> '); |
| 349 |
groupHeader.append( |
| 350 |
$('<button type="button" class="btn btn-default btn-xs" style="margin-left: 10px;">' + |
| 351 |
'<i class="fa fa-trash"></i> Remove group</button>') |
| 352 |
.on('click', function() { removeServicingGroup(groupIndex); }) |
| 353 |
); |
| 354 |
groupDiv.append(groupHeader); |
| 355 |
|
| 356 |
if (group.length === 0) { |
| 357 |
groupDiv.append('<div class="hint" style="margin-left: 20px;">Empty group</div>'); |
| 358 |
} |
| 359 |
|
| 360 |
group.forEach(function(instruction, instructionIndex) { |
| 361 |
var instructionDiv = $('<div class="servicing_instruction" style="margin-left: 20px; margin-bottom: 5px;"></div>'); |
| 362 |
|
| 363 |
var typeSelect = $('<select style="width: 100px; margin-right: 5px;"></select>') |
| 364 |
.append('<option value="LVC"' + (instruction.type === 'LVC' ? ' selected' : '') + '>LVC (Coded)</option>') |
| 365 |
.append('<option value="LVT"' + (instruction.type === 'LVT' ? ' selected' : '') + '>LVT (Text)</option>') |
| 366 |
.on('change', function() { |
| 367 |
var newType = $(this).val(); |
| 368 |
var valueInput = $(this).next(); |
| 369 |
updateInstruction(groupIndex, instructionIndex, newType, valueInput.val()); |
| 370 |
renderServicingInstructions(); // Re-render to update input type |
| 371 |
}); |
| 372 |
|
| 373 |
var valueInput; |
| 374 |
if (instruction.type === 'LVC' && servicingInstructionAVs.length > 0) { |
| 375 |
valueInput = $('<select style="width: 300px; margin-right: 5px;"></select>'); |
| 376 |
valueInput.append('<option value="">Select...</option>'); |
| 377 |
servicingInstructionAVs.forEach(function(av) { |
| 378 |
valueInput.append( |
| 379 |
$('<option></option>') |
| 380 |
.attr('value', av.value) |
| 381 |
.text(av.label) |
| 382 |
.prop('selected', av.value === instruction.value) |
| 383 |
); |
| 384 |
}); |
| 385 |
} else { |
| 386 |
valueInput = $('<input type="text" style="width: 300px; margin-right: 5px;" />') |
| 387 |
.val(instruction.value); |
| 388 |
} |
| 389 |
|
| 390 |
valueInput.on('change', function() { |
| 391 |
updateInstruction(groupIndex, instructionIndex, typeSelect.val(), $(this).val()); |
| 392 |
}); |
| 393 |
|
| 394 |
var removeBtn = $('<button type="button" class="btn btn-default btn-xs">' + |
| 395 |
'<i class="fa fa-trash"></i> Remove</button>') |
| 396 |
.on('click', function() { removeInstructionFromGroup(groupIndex, instructionIndex); }); |
| 397 |
|
| 398 |
instructionDiv.append(typeSelect); |
| 399 |
instructionDiv.append(valueInput); |
| 400 |
instructionDiv.append(removeBtn); |
| 401 |
groupDiv.append(instructionDiv); |
| 402 |
}); |
| 403 |
|
| 404 |
// Determine what can be added to this group |
| 405 |
var hasLVC = group.some(function(inst) { return inst.type === 'LVC'; }); |
| 406 |
var hasLVT = group.some(function(inst) { return inst.type === 'LVT'; }); |
| 407 |
var canAddMore = group.length < 2; |
| 408 |
|
| 409 |
if (canAddMore) { |
| 410 |
var btnText = '<i class="fa fa-plus"></i> Add instruction'; |
| 411 |
if (hasLVC && !hasLVT) { |
| 412 |
btnText += ' (LVT only)'; |
| 413 |
} else if (hasLVT && !hasLVC) { |
| 414 |
btnText += ' (LVC only)'; |
| 415 |
} else if (group.length === 0) { |
| 416 |
btnText += ' to this group'; |
| 417 |
} |
| 418 |
|
| 419 |
var addInstructionBtn = $('<button type="button" class="btn btn-default btn-xs" style="margin-left: 20px; margin-top: 5px;">' + btnText + '</button>') |
| 420 |
.on('click', function() { |
| 421 |
// Determine default type based on what's already in the group |
| 422 |
var defaultType = 'LVC'; |
| 423 |
if (hasLVC && !hasLVT) defaultType = 'LVT'; |
| 424 |
else if (hasLVT && !hasLVC) defaultType = 'LVC'; |
| 425 |
addInstructionToGroup(groupIndex, defaultType); |
| 426 |
}); |
| 427 |
groupDiv.append(addInstructionBtn); |
| 428 |
} else { |
| 429 |
var maxMsg = $('<div class="hint" style="margin-left: 20px; margin-top: 5px; color: #666;">' + |
| 430 |
'Maximum 2 instructions per group (one LVC + one LVT)</div>'); |
| 431 |
groupDiv.append(maxMsg); |
| 432 |
} |
| 433 |
|
| 434 |
container.append(groupDiv); |
| 435 |
}); |
| 436 |
} |
| 437 |
|
| 438 |
function updateJSON() { |
| 439 |
// Filter out empty groups and instructions with empty values |
| 440 |
var filtered = servicingInstructionGroups |
| 441 |
.map(function(group) { |
| 442 |
return group.filter(function(instruction) { |
| 443 |
return instruction.value && instruction.value.trim() !== ''; |
| 444 |
}); |
| 445 |
}) |
| 446 |
.filter(function(group) { |
| 447 |
return group.length > 0; |
| 448 |
}); |
| 449 |
|
| 450 |
var json = JSON.stringify(filtered); |
| 451 |
$('#servicing_instruction_groups_json').val(json); |
| 452 |
} |
| 228 |
</script> |
453 |
</script> |
| 229 |
[% Asset.css("css/addbiblio.css") | $raw %] |
454 |
[% Asset.css("css/addbiblio.css") | $raw %] |
| 230 |
</head> |
455 |
</head> |
|
Lines 716-721
Link Here
|
| 716 |
<label for="order_vendornote">Vendor note: </label> |
941 |
<label for="order_vendornote">Vendor note: </label> |
| 717 |
<textarea id="order_vendornote" cols="30" rows="3" name="order_vendornote">[% IF ( order_vendornote ) %][% order_vendornote | html %][% END %]</textarea> |
942 |
<textarea id="order_vendornote" cols="30" rows="3" name="order_vendornote">[% IF ( order_vendornote ) %][% order_vendornote | html %][% END %]</textarea> |
| 718 |
</li> |
943 |
</li> |
|
|
944 |
<li> |
| 945 |
<label for="servicing_instructions">Servicing instructions: </label> |
| 946 |
<div id="servicing_instructions_container"> |
| 947 |
<!-- Servicing instruction groups will be populated here --> |
| 948 |
</div> |
| 949 |
<button type="button" id="add_servicing_group" class="btn btn-default btn-xs"> <i class="fa fa-plus"></i> Add servicing group </button> |
| 950 |
<input type="hidden" id="servicing_instruction_groups_json" name="servicing_instruction_groups_json" value="" /> |
| 951 |
<div class="hint">EDIFACT servicing instructions (GIR LVT/LVC). Each group can contain multiple coded (LVC) and/or free-text (LVT) instructions that work together.</div> |
| 952 |
</li> |
| 719 |
<li> |
953 |
<li> |
| 720 |
<label for="estimated_delivery_date">Estimated delivery date: </label> |
954 |
<label for="estimated_delivery_date">Estimated delivery date: </label> |
| 721 |
<input type="text" id="estimated_delivery_date" size="10" name="estimated_delivery_date" class="flatpickr" value="[% estimated_delivery_date | html %]" /> |
955 |
<input type="text" id="estimated_delivery_date" size="10" name="estimated_delivery_date" class="flatpickr" value="[% estimated_delivery_date | html %]" /> |
| 722 |
- |
|
|