push only for upload attachment and project code but not working the autocomplete yet

This commit is contained in:
rowell_m_soriano 2026-02-13 15:56:56 +08:00
parent 0612679648
commit 6e0551707e
26 changed files with 755 additions and 6975 deletions

View File

@ -22,6 +22,8 @@ namespace CPRNIMS.Domain.Contracts.Items
Task<List<ItemColor>> GetItemColor(ItemDto itemDto);
Task<List<UnitOfMessure>> GetItemUOM(ItemDto itemDto);
Task<List<NotifUserKey>> GetNotifUserKey(ItemDto itemDto);
Task<List<ProjectCodes>> GetProjectCode();
Task<List<ProjectCodes>> GetProjectCodeByTerm(string? fileName);
Task<(long, long)> GetPRNo();
Task<ResponseObject> PostPurchRequest(ItemDto itemDto);
Task<ItemCodeDto> PostPutItem(ItemCodeDto itemDto);

View File

@ -268,7 +268,7 @@ namespace CPRNIMS.Domain.Services.Items
else
{
existing.OrigFileName = attach.OrigFileName;
existing.FileName = Guid.NewGuid().ToString();
existing.FileName = attach.FileName;
}
await _dbContext.SaveChangesAsync();
}
@ -316,5 +316,19 @@ namespace CPRNIMS.Domain.Services.Items
return allItems ?? new List<NotifUserKey>();
}
public async Task<List<ProjectCodes>> GetProjectCode()
{
return await _dbContext.ProjectCodes
.AsNoTracking()
.ToListAsync();
}
public async Task<List<ProjectCodes>> GetProjectCodeByTerm(string? term)
{
return await _dbContext.ProjectCodes
.AsNoTracking()
.Where(p => EF.Functions.Like(p.ProjectCode, $"%{term}%"))
.ToListAsync();
}
}
}

View File

@ -21,6 +21,7 @@ namespace CPRNIMS.Domain.UIContracts.Items
Task<List<ItemVM>> GetItemUOM(User user, ItemVM viewModel);
Task<List<ItemVM>> GetDepartment(User user, ItemVM viewModel);
Task<List<ItemVM>> GetItemCart(User user, ItemVM viewModel);
Task<List<ItemVM>?> GetProjectCode(User user, ItemVM viewModels);
Task<ItemVM> PostPurchRequest(User user, ItemVM viewModel);
Task<ItemVM> PostPutItem(User user, ItemVM viewModel);
Task<ItemVM> PutItemDetail(User user, ItemVM viewModel);

View File

@ -40,6 +40,7 @@ namespace CPRNIMS.Domain.UIContracts.PR
Task<PRVM> PRItemRemoval(User user, PRVM viewModel);
Task<PRVM> ApprovedSelectedPRItem(User user, PRVM viewModel);
Task<PRVM> PostPutProjectCode(User user, PRVM viewModel);
Task<PRVM> PostPutAttachment(User user, PRVM prVM);
#endregion
}
}

View File

@ -129,6 +129,11 @@ namespace CPRNIMS.Domain.UIServices.Items
}
#endregion
#region Get Method
public async Task<List<ItemVM>> GetProjectCode(Infrastructure.Models.Account.User user, ItemVM viewModel)
{
return await SendGetApiRequest(user, viewModel,
_configuration["LLI:NonInvent:ItemMgmt:GetProjectCode"]);
}
public async Task<List<ItemVM>> GetItemDetail(Infrastructure.Models.Account.User user,
ItemVM viewModel)
{

View File

@ -264,6 +264,12 @@ namespace CPRNIMS.Domain.UIServices.PR
return await SendPostApiRequest(user, viewModel,
_configuration["LLI:NonInvent:PRMgmt:PostPutProjectCode"]);
}
public async Task<PRVM> PostPutAttachment(User user, PRVM prVM)
{
return await SendPostApiRequest(user, prVM,
_configuration["LLI:NonInvent:PRMgmt:PostPutAttachment"]);
}
#endregion
}
}

View File

@ -12,6 +12,7 @@ namespace CPRNIMS.Infrastructure.Entities.Purchasing
{
[Key]
public long PRDetailsId { get; set; }
public long PRId { get; set; }
public long ItemCodeId { get; set; }
public short ItemClassId { get; set; }
public string? Department { get; set; }
@ -38,5 +39,7 @@ namespace CPRNIMS.Infrastructure.Entities.Purchasing
public string? AttestedBy { get; set; }
public string? ApprovedBy { get; set; }
public string? ProjectCode { get; set; }
public string? FileName { get; set; }
public string? OrigFileName { get; set; }
}
}

View File

@ -32,9 +32,9 @@ namespace CPRNIMS.Infrastructure.Entities.Purchasing
public string? UOMName { get; set; }
public string? ItemColorName { get; set; }
public long PRNo { get; set; }
public long PRId { get; set; }
public string? Remarks { get; set; }
public DateTime DateNeeded { get; set; }
//public bool Queue { get; set; }
public string? AggreItemName { get; set; }
}
}

View File

@ -1,13 +1,12 @@
using CPRNIMS.Domain.Contracts.Items;
using CPRNIMS.Domain.Contracts.SMTP;
using CPRNIMS.Domain.Services;
using CPRNIMS.Infrastructure.Dto.Items;
using CPRNIMS.Infrastructure.Entities.Items;
using CPRNIMS.Infrastructure.Helper;
using CPRNIMS.Infrastructure.Models.Common;
using CPRNIMS.Infrastructure.ViewModel.Common;
using CPRNIMS.Infrastructure.ViewModel.Items;
using CPRNIMS.WebApi.Controllers.Base;
using Google.Apis.Http;
using Microsoft.AspNetCore.Mvc;
using System.Text;
@ -172,6 +171,12 @@ namespace CPRNIMS.WebApi.Controllers.Items
});
}
}
[HttpPost("GetProjectCode")]
public async Task<IActionResult> GetProjectCode(ItemCodeDto itemDto)
{
var results = await _item.GetProjectCodeByTerm(itemDto.FileName);
return Ok( new {message="success", messCode=1, data= results });
}
[HttpPost("GetDepartment")]
public async Task<IActionResult> GetDepartment(ItemCodeDto itemDto)

View File

@ -1,5 +1,7 @@
using CPRNIMS.Domain.Contracts.PR;
using CPRNIMS.Domain.Contracts.Items;
using CPRNIMS.Domain.Contracts.PR;
using CPRNIMS.Domain.Services;
using CPRNIMS.Infrastructure.Dto.Items;
using CPRNIMS.Infrastructure.Dto.PO;
using CPRNIMS.Infrastructure.Dto.PR;
using CPRNIMS.Infrastructure.Entities.PO;
@ -12,6 +14,7 @@ using CPRNIMS.WebApi.Controllers.Base;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using System.Reflection.Metadata.Ecma335;
using System.Text;
@ -23,17 +26,30 @@ namespace CPRNIMS.WebApi.Controllers.PR
private readonly IPRequest _pRequest;
private readonly SMTPHelper _smptHelper;
private readonly IConfiguration _config;
private readonly IItem _item;
public PRMgmtController(ErrorMessageService errorMessageService,
IWebHostEnvironment webHostEnvironment, SMTPHelper sMTPHelper,
IConfiguration configuration, IPRequest pRequest) :
IConfiguration configuration, IPRequest pRequest, IItem item) :
base(errorMessageService, webHostEnvironment, configuration)
{
_config = configuration;
_smptHelper = sMTPHelper;
_pRequest = pRequest;
_item = item;
}
#region POST PUT
[HttpPost("PostPutAttachment")]
public async Task<IActionResult> PostPutAttachment([FromBody] PRVM PRDto)
{
var item = new AttachmentRequest()
{
PRId=PRDto.PRId,
FileName=PRDto.FileName,
OrigFileName=PRDto.OrigFileName,
};
await _item.PostPutAttachment(item);
return Ok(new { messCode = 1, message = "success"});
}
[HttpPost("ApprovedSelectedPRItem")]
public async Task<IActionResult> ApprovedSelectedPRItem([FromBody] PRVM PRDto)
{

View File

@ -255,6 +255,13 @@ namespace CPRNIMS.WebApps.Controllers.Items
#endregion
#region Get
public async Task<IActionResult> GetProjectCode(string term)
{
var item = new ItemVM();
item.FileName = term;
response = await _item.GetProjectCode(GetUser(), item);
return GetResponse(response);
}
public async Task<IActionResult> GetImageFileIds()
{
try

View File

@ -21,24 +21,28 @@ namespace CPRNIMS.WebApps.Controllers.PR
_pRequest = pRequest;
}
#region Get
private async Task<IActionResult> GetProductFile(string fileName)
[HttpGet]
public async Task<IActionResult> GetPRAttachment(string fileName)
{
try
{
// Validate filename
if (string.IsNullOrWhiteSpace(fileName) || fileName.Contains(".."))
{
return BadRequest("Invalid file name");
// Return a 400 Bad Request so the frontend 'catch' or '!response.ok' triggers
return BadRequest(new { success = false, message = "File does not exist!" });
}
var uploadsPath = Path.Combine(_webHostEnvironment.WebRootPath, "Content/Uploads", "PRAttachment");
var filePath = Path.Combine(uploadsPath, fileName);
ContentTypeHelper.ValidateFile(filePath, uploadsPath);
if (!System.IO.File.Exists(filePath))
{
return NotFound(new { success = false, message = "File not found on server." });
}
// Stream the file instead of loading entirely into memory
// Stream the file for better performance
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var contentType = ContentTypeHelper.GetContentType(fileName);
var contentType = "application/octet-stream"; // Or use a provider to get actual type
return File(fileStream, contentType, fileName, enableRangeProcessing: true);
}
@ -47,6 +51,7 @@ namespace CPRNIMS.WebApps.Controllers.PR
return StatusCode(500, "Error retrieving file");
}
}
public async Task<IActionResult> GetProjectCodes(PRVM viewModels)
{
response = await _pRequest.GetProjectCodes(GetUser(), viewModels);
@ -150,6 +155,79 @@ namespace CPRNIMS.WebApps.Controllers.PR
}
#endregion
#region POST PUT
public async Task<IActionResult?> UploadAttachment(IFormFile? file, [FromForm] string? oldFileName,
[FromForm] long prId)
{
var uploadsPath = Path.Combine(
_webHostEnvironment.WebRootPath,
"Content", "Uploads", "PRAttachment");
Directory.CreateDirectory(uploadsPath);
// If no new file uploaded, return old filename
if (file == null)
return BadRequest(new { success = false, message = "File does not exist!" });
// Delete old file if exists
if (!string.IsNullOrWhiteSpace(oldFileName))
{
await DeleteAttachmentAsync(oldFileName);
}
// Validate file extension
var allowedExtensions = new[] { ".csv", ".xlsx", ".xls", ".pdf" };
var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!allowedExtensions.Contains(fileExtension))
{
throw new InvalidOperationException("Invalid file type. Only CSV, Excel, and PDF files are allowed.");
}
// Validate file size (5MB max)
if (file.Length > 5 * 1024 * 1024)
{
throw new InvalidOperationException("File size exceeds 5MB limit.");
}
// Generate new unique filename with original extension
var newFileName = $"{Guid.NewGuid()}{fileExtension}";
var newFilePath = Path.Combine(uploadsPath, newFileName);
// Save new file
await using var stream = new FileStream(newFilePath, FileMode.Create);
await file.CopyToAsync(stream);
var prVM = new PRVM()
{
FileName = newFileName,
OrigFileName = file.FileName,
PRId = prId
};
var prResponse = await _pRequest.PostPutAttachment(GetUser(),prVM);
if (prResponse.messCode == 0) {
return BadRequest(new { success = false, message = "File does not exist!"});
}
// Return only filename (NOT full path)
return Json(new { success = true, message = "Attachment successfully uploaded!", newFileName = newFileName });
}
private async Task DeleteAttachmentAsync(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName))
return;
var uploadsPath = Path.Combine(
_webHostEnvironment.WebRootPath,
"Content", "Uploads", "PRAttachment");
var filePath = Path.Combine(uploadsPath, fileName);
if (System.IO.File.Exists(filePath))
{
await Task.Run(() => System.IO.File.Delete(filePath));
}
}
public async Task<IActionResult> PostPutProjectCode([FromBody] PRVM viewModel)
{
var postPutItem = await _pRequest.PostPutProjectCode(GetUser(), viewModel);

View File

@ -113,7 +113,7 @@
rows="4"
placeholder="Add any additional notes or comments here..."></textarea>
<div class="form-text">
<span id="charCount">0</span>/500 characters
<span id="charCount">0</span>/100 characters
</div>
</div>

View File

@ -74,8 +74,8 @@
</div>
</div>
</div>
<link href="~/css/pr/TrackingV2.css" rel="stylesheet" />
<link href="~/css/pr/buttonstyle.css" rel="stylesheet" />
<link href="~/css/pr/TrackingV3.css" rel="stylesheet" />
<link href="~/css/pr/ButtonStyleV2.css" rel="stylesheet" />
@await Html.PartialAsync("PagesView/PR/_PRTracking")
<script src="~/JsFunctions/PR/PRV6.js"></script>
@await Html.PartialAsync("PagesView/PR/_PRScripts")

View File

@ -5,7 +5,7 @@
</div>
<input hidden id="roleRights" value="@ViewBag.UserRoles" />
<link href="~/css/canvass/itemfortagging.css" rel="stylesheet" />
<link href="~/css/pr/buttonstyle.css" rel="stylesheet" />
<link href="~/css/pr/ButtonStyleV2.css" rel="stylesheet" />
<link href="~/css/common/rowhighlighter.css" rel="stylesheet" />
<script src="~/JsFunctions/Canvass/canvassColumnV9.js"></script>
<script src="~/JsFunctions/Canvass/canvassVarV3.js"></script>

View File

@ -2,6 +2,7 @@
<div id="loader" class="loader"></div>
</div>
<link href="~/css/common/rowhighlighter.css" rel="stylesheet" />
<script src="~/jsfunctions/pr/PRColumnV7.js"></script>
<script src="~/jsfunctions/pr/PRViewV5.js"></script>
<script src="~/jsfunctions/pr/PRPutPost.js"></script>

View File

@ -337,60 +337,112 @@
</div>
<div id="printableRelatedItem">
<div class="modal-body">
<!-- PR Info Section -->
<div class="p-2 mb-3 rounded border shadow-sm bg-light pr-info-section">
<div class="row mb-2">
<!-- PR No -->
<div class="col-md-5">
<small class="text-muted">P.R. No</small>
<div id="label-pr-prNo" class="fw-bold text-dark"></div>
<!-- PR SUMMARY CARD -->
<div class="card border-0 shadow-sm mb-4 pr-info-section">
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<div class="text-muted small">P.R. No</div>
<div id="label-pr-prNo" class="fw-bold fs-6"></div>
</div>
<!-- PR By -->
<div class="col-md-5">
<small class="text-muted">P.R. By</small>
<div id="label-prby" class="fw-bold text-dark"></div>
<div class="col-md-3">
<div class="text-muted small">Date Needed</div>
<div id="label-pr-dateNeeded" class="fw-semibold text-danger"></div>
</div>
<!-- Department -->
<div class="col-md-5">
<small class="text-muted">Department</small>
<div id="label-pr-Department" class="fw-bold text-dark"></div>
<div class="col-md-3">
<div class="text-muted small">Department</div>
<div id="label-pr-Department" class="fw-semibold"></div>
</div>
<div class="col-md-3">
<div class="text-muted small">Project Code</div>
<div id="label-pr-ProjectCode" class="fw-semibold"></div>
</div>
<div class="col-md-3">
<div class="text-muted small">Requested By</div>
<div id="label-prby" class="fw-semibold"></div>
</div>
<div class="col-md-3">
<div class="text-muted small">Attested By</div>
<div id="label-pr-attestedBy" class="fw-semibold"></div>
</div>
<div class="col-md-3">
<div class="text-muted small">Approved By</div>
<div id="label-pr-approvedBy" class="fw-semibold"></div>
</div>
<div class="col-md-3">
<div class="text-muted small">Remarks</div>
<div id="label-pr-remarks"
class="bg-light rounded p-2 small"></div>
</div>
</div>
<div class="col-md-5">
<small class="text-muted">Project Code</small>
<div id="label-pr-ProjectCode" class="fw-bold text-dark"></div>
</div>
</div>
<div class="row mb-2">
<!-- Attested By -->
<div class="col-md-4">
<small class="text-muted">Attested By</small>
<div id="label-pr-attestedBy" class="fw-bold text-dark"></div>
<!-- SELECTION SUMMARY -->
<div class="d-flex align-items-center mb-3 gap-2">
<div>
<span class="fw-semibold">Selected Items:</span>
<span id="totalSelected" class="badge bg-danger ms-2">0</span>
</div>
<!-- Approved By -->
<div class="col-md-4">
<small class="text-muted">Approved By</small>
<div id="label-pr-approvedBy" class="fw-bold text-dark"></div>
<div>
<button id="btnDownloadAttachment"
class="btn btn-sm btn-outline-teal d-none"
onclick="downloadPRAttachment()">
📎Export
</button>
<input hidden id="fileName" />
<input hidden id="origFileName" />
<input hidden id="prId" />
</div>
<!-- File Attachment Section -->
<div class="d-flex align-items-center gap-2 flex-grow-1">
<!-- File Input -->
<div class="file-upload-wrapper position-relative">
<input type="file"
class="form-control"
id="fileAttachment"
name="fileAttachment"
accept=".csv,.xlsx,.xls,.pdf"
style="max-width: 300px;" />
</div>
<!-- File Info Badge (shows when file selected) -->
<div id="fileInfoBadge" class="d-none">
<span class="badge bg-success-subtle text-success border border-success px-3 py-2">
<i class="bi bi-file-earmark-check me-1"></i>
<span id="fileNameDisplay" class="me-2"></span>
<small class="text-muted">(<span id="fileSizeDisplay"></span>)</small>
<button type="button"
class="btn-close btn-close-sm ms-2"
onclick="clearFileAttachment()"
style="font-size: 0.7rem; vertical-align: middle;"
title="Remove file"></button>
</span>
</div>
<!-- Upload/Update Button (shows when file selected) -->
<div id="btnUploadContainer" class="d-none ms-auto">
<button type="button"
class="btn btn-success btn-sm"
onclick="uploadAttachment()">
<i class="bi bi-cloud-upload me-1"></i>
<span id="btnUploadText">Upload Attachment</span>
</button>
</div>
<!-- Date Needed -->
<div class="col-md-4">
<small class="text-muted">Date Needed</small>
<div id="label-pr-dateNeeded" class="fw-bold text-dark"></div>
</div>
</div>
<!-- Requestor Remarks -->
<div class="row">
<div class="col-md-12">
<small class="text-muted">Remarks</small>
<div id="label-pr-remarks" class="fw-bold text-dark"></div>
</div>
</div>
</div>
<label for="totalSelected" style="font-size:medium;">Total Selected : </label>
<label id="totalSelected"
style="margin-bottom:20px; width:50px; font-weight:bold; color:red;"></label>
<br />
<table id="PRdataTable"
class="row-border" style="width: 100%;">
<colgroup>
@ -422,15 +474,31 @@
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="resetIsApproval()" data-bs-dismiss="modal">Back</button>
<button type="button" id="btnPrintPR" onclick="printPRItem();" class="btn btn-warning">Print</button>
<button id="btnSubmitItem" type="button"
class="btn btn-success" onclick="approvedSelectedPRItem();">
Submit
<div class="modal-footer pr-modal-footer">
<button type="button"
class="btn btn-outline-secondary"
onclick="resetIsApproval()"
data-bs-dismiss="modal">
Back
</button>
<button type="button"
id="btnPrintPR"
onclick="printPRItem();"
class="btn btn-teal-warning">
🖨 Print
</button>
<button id="btnSubmitItem"
type="button"
class="btn btn-teal-primary"
onclick="approvedSelectedPRItem();">
✔ Approve Selected
</button>
</div>
</div>
</div>
</div>
<input hidden id="roleRights" value="@ViewBag.UserRoles" />

View File

@ -247,6 +247,17 @@ document.getElementById('fileAttachment').addEventListener('change', function (e
filePreview.classList.remove('d-none');
}
});
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
function clearFileAttachment() {
document.getElementById('fileAttachment').value = '';
document.getElementById('filePreview').classList.add('d-none');
}
function clearFormData() {
document.getElementById('dateNeeded').value = '';
document.getElementById('projectCode').value = '';
@ -256,14 +267,10 @@ function clearFormData() {
clearFileAttachment();
document.getElementById('charCount').textContent = '0';
}
function clearFileAttachment() {
document.getElementById('fileAttachment').value = '';
document.getElementById('filePreview').classList.add('d-none');
}
document.getElementById('requestorRemarks').addEventListener('input', function (e) {
const charCount = document.getElementById('charCount');
const currentLength = e.target.value.length;
const maxLength = 500;
const maxLength = 100;
charCount.textContent = currentLength;
@ -279,10 +286,4 @@ document.getElementById('requestorRemarks').addEventListener('input', function (
charCount.classList.remove('text-danger');
}
});
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}

View File

@ -1,32 +1,50 @@
async function setupProjectCodeAutocomplete() {
const input = document.getElementById('projectCode');
let debounceTimer;
function populateGetProjectCode() {
$("#projectCode").autocomplete({
source: function (request, response) {
$.ajax({
url:'/ItemMgmt/GetProjectCode',
data: { query: request.term },
success: function (result) {
if (result && result.success && Array.isArray(result.data)) {
input.addEventListener('input', function (e) {
clearTimeout(debounceTimer);
const searchTerm = e.target.value;
if (searchTerm.length < 2) return;
debounceTimer = setTimeout(async () => {
try {
// Replace with your actual API endpoint
const response = await fetch(`/api/projects/search?term=${encodeURIComponent(searchTerm)}`);
const projects = await response.json();
const datalist = document.getElementById('projectCodeList');
datalist.innerHTML = '';
projects.forEach(project => {
const option = document.createElement('option');
option.value = project.code;
option.textContent = `${project.code} - ${project.name}`;
datalist.appendChild(option);
});
} catch (error) {
console.error('Error fetching project codes:', error);
var formattedData = result.data.map(item => ({
label: item.label || '',
value: item.value !== undefined && item.value !== null ? item.value.toString() : '',
value2: item.value2 !== undefined && item.value2 !== null ? item.value2.toString() : '',
value3: item.value3 !== undefined && item.value3 !== null ? item.value3.toString() : '',
value4: item.value4 !== undefined && item.value4 !== null ? item.value4.toString() : ''
}));
response(formattedData);
} else {
console.error('Invalid data format received:', result);
response([]);
}
}
});
},
minLength: 2,
select: function (event, ui) {
$('#projectCodeList').val(ui.item.label);
$('#supplierId').val(ui.item.value);
return false;
},
focus: function (event, ui) {
event.preventDefault();
},
open: function () {
var dropdown = $(".ui-autocomplete");
dropdown.css({
"max-height": "200px",
"overflow-y": "auto"
});
},
messages: {
noResults: '',
results: function (count) {
return count + (count > 1 ? ' results' : ' result');
}
}
}, 300);
});
}
function populateItemCategSelect() {
@ -420,6 +438,7 @@ function inputItemPopulation() {
}
function showModal() {
popltDeprtmntChargeTo();
populateGetProjectCode();
$('#addDateNeeded').modal('show');
$('#addDateNeeded').css('z-index', 1060);
}

View File

@ -19,8 +19,6 @@
};
});
console.log(requestData);
var DateNeeded = document.getElementById('dateNeeded').value;
if (!DateNeeded) {
@ -234,91 +232,6 @@ function sendUpdateRequest(data, loader) {
}
});
}
//function postPutPurchase() {
// loader = $('#overlay, #loader').css('z-index', 1060);
// isValid = true;
// var Remarks = document.getElementById('requestorRemarks').value;
// var selectedCheckboxes = $('.selectedItem-checkbox:checked');
// var requestData = [];
// selectedCheckboxes.each(function () {
// var $row = $(this).closest('tr');
// var rowIndex = itemTable.row($row).index();
// var rowData = itemTable.row(rowIndex).data();
// var itemCartId = rowData.itemCartId;
// var itemNo = rowData.itemNo;
// var qty = $row.find('.editable-qty').val() || rowData.qty;
// if (parseFloat(qty) === 0 || isNaN(parseFloat(qty))) {
// isValid = false;
// showToast('warning', "Please input a valid qty for ItemNo# " + itemNo + "!", ' warning', 4000);
// $(modalId).modal('hide');
// return false;
// }
// var itemData = {
// itemCartId: itemCartId,
// ItemNo: itemNo,
// qty: qty
// };
// requestData.push(itemData);
// });
// if (selectedCheckboxes.length <= 0) {
// showToast('warning', 'No selected item!', 'Item cart failed', 4000);
// return;
// } else {
// var DateNeeded = document.getElementById('dateNeeded').value;
// if (!DateNeeded) {
// showToast('warning', 'Please choose date needed!', 'P.R. submission failed', 4000);
// confirmUpdateListener = false;
// return;
// }
// if (RequestTypeId == 3) {
// updateDepartmentId();
// var ChargeTo = document.getElementById('departmentId').value;
// if (!ChargeTo) {
// showToast('warning', 'Please choose a department to be in charge of!', 'P.R. submission failed', 4000);
// confirmUpdateListener = false;
// return;
// }
// }
// }
// showConfirmation({
// title: 'Purchasing Requisition',
// message: 'Are you sure you want to proceed? This action cannot be undone.',
// type: 'warning',
// confirmText: 'Yes',
// cancelText: 'No'
// }).then((confirmed) => {
// if (confirmed) {
// $.ajax({
// url: '/ItemMgmt/PostPurchRequest',
// type: 'POST',
// data: { ItemCartIds: requestData, DateNeeded: DateNeeded, RequestTypeId: RequestTypeId, ChargeTo: ChargeTo, Remarks: Remarks },
// success: function (response) {
// if (response.success) {
// itemTable.ajax.reload();
// $('#addDateNeeded').modal('hide');
// showToast('success', 'P.R. Successfully Created!', 'Success', 4000);
// var totalSelectedLabel = $('#totalSelected');
// totalSelectedLabel.text('');
// } else {
// itemTable.ajax.reload();
// showToast('error', response.response, title + ' failed', 4000);
// }
// },
// beforeSend: function () {
// loader.show();
// },
// complete: function () {
// loader.hide();
// }
// });
// }
// });
//}
function AddToCart(isUpdated) {
var loader = $('#overlay, #loader');
var ItemNo = $('#itemNo').val();

View File

@ -122,3 +122,151 @@ function submitItem() {
});
}
}
const fileInput = document.getElementById('fileAttachment');
const fileInfoBadge = document.getElementById('fileInfoBadge');
const fileNameDisplay = document.getElementById('fileNameDisplay');
const fileSizeDisplay = document.getElementById('fileSizeDisplay');
const btnUploadContainer = document.getElementById('btnUploadContainer');
const btnUploadText = document.getElementById('btnUploadText');
let selectedFile = null;
let existingAttachment = false; // Set this to true if there's already an attachment
// File input change event
fileInput.addEventListener('change', function (e) {
const file = e.target.files[0];
if (file) {
// Validate file size (5MB max)
const maxSize = 5 * 1024 * 1024; // 5MB in bytes
if (file.size > maxSize) {
showToast('warning', 'File size exceeds 5MB. Please choose a smaller file.', 'File Upload', 4000);
e.target.value = '';
return;
}
// Validate file type
const allowedExtensions = ['csv', 'xlsx', 'xls', 'pdf'];
const fileExtension = file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(fileExtension)) {
showToast('warning', 'Invalid file type. Please upload CSV, Excel, or PDF files only.', 'File Upload', 4000);
e.target.value = '';
return;
}
// Store the selected file
selectedFile = file;
// Show file info and upload button
showFileInfo(file);
showUploadButton();
}
});
// Show file information
function showFileInfo(file) {
fileNameDisplay.textContent = truncateFileName(file.name, 30);
fileSizeDisplay.textContent = formatFileSize(file.size);
fileInfoBadge.classList.remove('d-none');
}
// Show upload button
function showUploadButton() {
// Change button text based on whether there's an existing attachment
if (existingAttachment) {
btnUploadText.innerHTML = '<i class="bi bi-arrow-repeat me-1"></i>Update Attachment';
} else {
btnUploadText.innerHTML = '<i class="bi bi-cloud-upload me-1"></i>Upload Attachment';
}
btnUploadContainer.classList.remove('d-none');
}
// Clear file attachment
function clearFileAttachment() {
fileInput.value = '';
selectedFile = null;
fileInfoBadge.classList.add('d-none');
btnUploadContainer.classList.add('d-none');
}
// Upload attachment function
function uploadAttachment() {
if (!selectedFile) {
showToast('warning', 'Please select a file first.', 'File Upload', 3000);
return;
}
const formData = new FormData();
formData.append('file', selectedFile);
formData.append('prId', $('#prId').val());
formData.append('oldFileName', $('#fileName').val());
// Show loading state
btnUploadText.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Uploading...';
document.querySelector('#btnUploadContainer button').disabled = true;
fetch('/PRMgmt/UploadAttachment', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast('success', 'Attachment uploaded successfully!', 'File Upload', 3000);
existingAttachment = true;
$('#fileName').val(data.newFileName)
// Update the download button if needed
document.getElementById('btnDownloadAttachment').classList.remove('d-none');
clearFileAttachment();
} else {
showToast('error', data.message || 'Upload failed. Please try again.', 'File Upload', 4000);
}
})
.catch(error => {
console.error('Upload error:', error);
showToast('error', 'An error occurred during upload.', 'File Upload', 4000);
})
.finally(() => {
// Reset button state
document.querySelector('#btnUploadContainer button').disabled = false;
showUploadButton(); // Restore button text
});
}
// Truncate long file names
function truncateFileName(fileName, maxLength) {
if (fileName.length <= maxLength) return fileName;
const extension = fileName.split('.').pop();
const nameWithoutExt = fileName.substring(0, fileName.lastIndexOf('.'));
const truncatedName = nameWithoutExt.substring(0, maxLength - extension.length - 4) + '...';
return truncatedName + '.' + extension;
}
// Format file size
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
// Initialize: Check if there's an existing attachment on page load
function checkExistingAttachment() {
// Replace with your logic to check if attachment exists
// For example, check if there's a filename in your hidden input
const existingFileName = document.getElementById('fileName').value;
if (existingFileName) {
existingAttachment = true;
document.getElementById('btnDownloadAttachment').classList.remove('d-none');
} else {
existingAttachment = false;
}
}

View File

@ -55,7 +55,6 @@
modal.modal('show');
}
function getApproverName(prDetailsId) {
PRDetailsId = prDetailsId;
$.ajax({
@ -90,106 +89,111 @@ function printPRItem() {
document.body.appendChild(iframe);
const doc = iframe.contentWindow.document;
doc.open();
doc.write('<html><head><title>Print</title>');
doc.write('<!DOCTYPE html><html><head>');
doc.write('<meta charset="UTF-8">');
// Copy all stylesheets INCLUDING Bootstrap
const stylesheets = document.querySelectorAll('link[rel="stylesheet"]');
stylesheets.forEach(sheet => {
doc.write(sheet.outerHTML);
});
// Copy all inline styles
const styles = document.querySelectorAll('style');
styles.forEach(style => {
doc.write(style.outerHTML);
});
// Add additional print styles
doc.write(`
<style>
@media print {
.pr-info-section .row {
display: flex !important;
flex-wrap: nowrap !important;
}
.pr-info-section .col-md-4 {
flex: 0 0 33.33% !important;
max-width: 33.33% !important;
}
table {
table-layout: fixed !important;
width: 125% !important;
}
/* Hide the 5th and 6th columns */
/* Hide the 5th and 6th columns */
table th:nth-child(5),
table th:nth-child(6),
table td:nth-child(5),
table td:nth-child(6) {
display: none !important;
}
table th:not(:nth-child(5)):not(:nth-child(6)),
table td:not(:nth-child(5)):not(:nth-child(6)) {
width: auto !important;
}
.page-break {
page-break-before: always;
}
}
/* Reduce overall print font */
body {
font-family: Roman, sans-serif;
font-size: 12px;
margin: 0;
padding: 10px;
}
.header {
font-family: Roman, sans-serif;
font-size: 14px;
font-weight: bold;
text-align: center;
margin-bottom: 5px;
padding: 5px 0;
}
/* Reduce vertical padding inside table rows */
table {
border-collapse: collapse;
font-size: 12px;
font-family: Roman, sans-serif;
}
table th,
table td {
padding: 2px 2px;
line-height: 1.2;
vertical-align: top;
border: .5px solid #808080 !important;
font-size: 10px !important;
}
/* Reduce PR Info Section font */
.pr-info-section {
font-size: 10px !important;
background: #f8f9fa !important;
border: 1px solid #dee2e6 !important;
padding: 8px !important;
margin-bottom: 10px !important;
}
.pr-info-section label,
.pr-info-section strong,
.pr-info-section .fw-bold {
font-size: 10px !important;
font-weight: bold !important;
margin-bottom: 5px !important;
page-break-inside: avoid !important;
}
/* Make PR info labels slightly smaller */
.pr-info-section small,
.pr-info-section .text-muted {
font-size: 9px !important;
color: #666 !important;
}
/* Hide DataTables controls */
.dataTables_length,
.dataTables_filter,
.dataTables_info,
.dataTables_paginate {
display: none !important;
/* Reduce table body font */
table {
font-size: 9px !important;
border-collapse: collapse;
}
/* Keep header slightly larger for readability */
th {
font-size: 11px !important;
}
/* Reduce table padding */
th, td {
padding: 3px 4px !important;
border: 1px #ddd;
text-align: left;
}
/* Ensure Bootstrap grid works in print */
.row {
display: flex !important;
flex-wrap: wrap !important;
margin-right: -10px !important;
margin-left: -10px !important;
}
.col-md-3 {
flex: 0 0 25% !important;
max-width: 25% !important;
padding-right: 10px !important;
padding-left: 10px !important;
}
.card {
border: 1px #ddd !important;
}
.card-body {
padding: 5px !important;
}
th {
background-color: #444 !important;
color: white !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
tbody tr:nth-child(even) {
background-color: #666 !important;
color: white !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
tbody tr:nth-child(odd) {
background-color: #f9f9f9 !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
@page {
margin: 0.5in;
}
}
</style>
`);
@ -197,13 +201,14 @@ function printPRItem() {
doc.write('</head><body>');
// Add the main header
doc.write('<div class="header">Purchase Requisition Details</div>');
doc.write('<div style="text-align: center; margin-bottom: 20px;"><h5>Purchase Requisition Details</h5></div>');
// Get the PR info section HTML
// Get the PR info section HTML and preserve its exact structure
const prInfoSection = document.querySelector('.pr-info-section');
let prInfoHTML = '';
if (prInfoSection) {
prInfoHTML = prInfoSection.outerHTML;
const clonedPrInfo = prInfoSection.cloneNode(true);
prInfoHTML = clonedPrInfo.outerHTML;
}
// Clone the printable content
@ -220,19 +225,48 @@ function printPRItem() {
// Get the table
const table = printContent.querySelector('table');
if (table) {
const tbody = table.querySelector('tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
const rowsPerPage = 50; // Reduced to accommodate PR info section
// Create table header HTML
const thead = table.querySelector('thead');
const tbody = table.querySelector('tbody');
// Remove unwanted columns from header
if (thead) {
const headerRows = thead.querySelectorAll('tr');
headerRows.forEach(row => {
const cells = Array.from(row.querySelectorAll('th'));
// Count total cells
const totalCells = cells.length;
// Remove Action column (last)
if (cells[totalCells - 1]) cells[totalCells - 1].remove();
// Remove Status column (second to last)
if (cells[totalCells - 2]) cells[totalCells - 2].remove();
// Remove checkbox column (first)
if (cells[0]) cells[0].remove();
});
}
// Remove unwanted columns from body rows
const rows = Array.from(tbody.querySelectorAll('tr'));
rows.forEach(row => {
const cells = Array.from(row.querySelectorAll('td'));
const totalCells = cells.length;
// Remove Action column (last)
if (cells[totalCells - 1]) cells[totalCells - 1].remove();
// Remove Status column (second to last)
if (cells[totalCells - 2]) cells[totalCells - 2].remove();
// Remove checkbox column (first)
if (cells[0]) cells[0].remove();
});
const rowsPerPage = 50;
const tableHeaderHTML = thead ? thead.outerHTML : '';
// Split into pages
for (let i = 0; i < rows.length; i += rowsPerPage) {
// Add page break for pages after the first
if (i > 0) {
doc.write('<div class="page-break"></div>');
doc.write('<div style="page-break-before: always;"></div>');
}
// Add PR info section to every page
@ -242,20 +276,8 @@ function printPRItem() {
const currentPageRows = rows.slice(i, i + rowsPerPage);
// Create table for current page
doc.write('<table class="row-border" style="width: 100%;">');
doc.write('<colgroup>');
doc.write('<col style="width:8%" />');
doc.write('<col style="width:30%" />');
doc.write('<col style="width:34%" />');
doc.write('<col style="width:8%" />');
doc.write('<col style="width:8%" />');
doc.write('<col style="width:12%" />');
doc.write('</colgroup>');
// Add table header
doc.write('<table>');
doc.write(tableHeaderHTML);
// Add table body with current page rows
doc.write('<tbody>');
currentPageRows.forEach(row => {
doc.write(row.outerHTML);
@ -264,7 +286,7 @@ function printPRItem() {
doc.write('</table>');
}
} else {
// Fallback: just add PR info and content
// Fallback
doc.write(prInfoHTML);
doc.write(printContent.innerHTML);
}
@ -275,16 +297,17 @@ function printPRItem() {
// Wait for the iframe to load before printing
iframe.onload = function () {
// Print the iframe content
// Small delay to ensure styles are loaded
setTimeout(function () {
iframe.contentWindow.print();
// Remove the iframe after printing
setTimeout(function () {
document.body.removeChild(iframe);
}, 1000);
}, 500);
};
}
function viewItemRemovalRemarks(data) {
PRDetailsId = data.prDetailsId;
ItemName = data.itemName;
@ -696,7 +719,8 @@ function viewPRDetails(data) {
document.getElementById('label-pr-approvedBy').innerHTML = data.approvedBy;
document.getElementById('label-pr-ProjectCode').innerHTML = data.projectCode;
console.log('data.projectCode', data.projectCode);
$('#prId').val(data.prId);
console.log('data.prId', data.prId);
document.getElementById('label-pr-dateNeeded').innerHTML = formatDate(data.dateNeeded);
@ -712,6 +736,27 @@ function viewPRDetails(data) {
emptyTable: "No record available"
},
initComplete: function () {
var api = this.api();
var response = api.ajax.json();
// 1. Find the first row that actually has a fileName
const rowWithFile = response?.data?.find(
row => row.fileName && row.fileName.trim() !== ''
);
// 2. If a file exists, update the hidden inputs and show the button
if (rowWithFile) {
$('#fileName').val(rowWithFile.fileName);
$('#origFileName').val(rowWithFile.origFileName || ''); // Use empty string if null
$('#btnDownloadAttachment').removeClass('d-none');
} else {
// Clear values and hide button if no attachment is found
$('#fileName').val('');
$('#origFileName').val('');
$('#btnDownloadAttachment').addClass('d-none');
}
// Keep your selection initialization
initializeTableSelection({
tableName: tableName,
dataTable: prDataTable,
@ -727,11 +772,43 @@ function viewPRDetails(data) {
});
},
columns: colItemList,
// responsive: true,
pageLength: 5,
lengthMenu: [[5, 10, 25, 50, 100, -1], [5, 10, 25, 50, 100, "All"]],
rowCallback: rowStatusColorCallback,
error: errorHandler
});
}
function downloadPRAttachment() {
let fileName = $('#fileName').val();
// Append parameters to URL for GET requests
const params = new URLSearchParams({ fileName: fileName });
const url = `/PRMgmt/GetPRAttachment?${params.toString()}`;
fetch(url, { method: 'GET' })
.then(response => {
if (!response.ok) {
// If backend returns 500 or 400, it won't be a blob
throw new Error("Failed to download file");
}
return response.blob();
})
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
})
.catch(error => {
console.error("Error downloading attachment:", error);
alert("Error downloading attachment. Please check if the file exists.");
});
}
function clearTextModal() {
if (UserRights == 'LLISCMAdmin' || UserRights == 'LTReceiver') {
document.getElementById('docTypeId').value = "";

View File

@ -18,3 +18,26 @@
outline: none;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
}
.pr-modal-footer {
background: #f1f5f9;
border-top: 1px solid #e2e8f0;
}
/* Teal primary button */
.btn-teal-primary {
background-color: #0f766e;
border-color: #0f766e;
color: #fff;
}
.btn-teal-primary:hover {
background-color: #115e59;
border-color: #115e59;
}
/* Soft warning */
.btn-teal-warning {
background-color: #f59e0b;
border-color: #f59e0b;
color: #fff;
}

View File

@ -133,6 +133,14 @@ textarea {
.modal-footer .btn {
min-width: 80px;
}
.pr-table-header {
background-color: #0f766e;
color: #fff;
}
.pr-table-header th {
border-color: #0f766e;
}
.form-row .col {
padding-left: 0;
@ -170,3 +178,53 @@ textarea {
transform: scale(1.05);
box-shadow: 0 4px 10px rgba(0,0,0,0.2)
}
.btn-outline-teal {
color: #0f766e;
border: 1px solid #0f766e;
background: transparent;
}
.btn-outline-teal:hover {
background-color: #0f766e;
color: #fff;
}
/* File upload styling */
.file-upload-wrapper {
position: relative;
}
#fileAttachment {
cursor: pointer;
}
#fileInfoBadge .badge {
font-size: 0.875rem;
font-weight: 400;
}
#fileInfoBadge .btn-close {
padding: 0;
opacity: 0.5;
}
#fileInfoBadge .btn-close:hover {
opacity: 1;
}
/* Smooth transitions */
#fileInfoBadge, #btnUploadContainer {
animation: fadeInRight 0.3s ease;
}
@keyframes fadeInRight {
from {
opacity: 0;
transform: translateX(-10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}