diff --git a/Areas/Inventory/Models/ItemModel.cs b/Areas/Inventory/Models/ItemModel.cs index 721709d..419103f 100644 --- a/Areas/Inventory/Models/ItemModel.cs +++ b/Areas/Inventory/Models/ItemModel.cs @@ -36,6 +36,9 @@ namespace PSTW_CentralSystem.Areas.Inventory.Models public string PartNumber { get; set; } = string.Empty; public int CreatedByUserId { get; set; } [ForeignKey("CreatedByUserId")] + + public DateTime CreateDate { get; set; } = DateTime.Now; + public virtual UserModel? CreatedBy { get; set; } [ForeignKey("CompanyId")] public virtual CompanyModel? Company { get; set; } diff --git a/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml b/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml index 3f3e846..3630487 100644 --- a/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml +++ b/Areas/Inventory/Views/InventoryMaster/QrMaster.cshtml @@ -952,41 +952,43 @@ async fetchItem(itemid) { try { const response = await fetch('/InvMainAPI/GetItem/' + itemid, { - method: 'POST',} - ); - if (response.ok) { - // this.thisItem = await response.json(); + method: 'POST', + }); + if (response.ok) { this.thisItem = await response.json(); // If the item is disposable, set the quantity to 1 by default, or to its current quantity if available if (this.thisItem.category === 'Disposable') { - this.quantity = 1; // Reset to 1 or default quantity when a disposable item is scanned - this.maxQuantity = this.thisItem.quantity; // Set maxQuantity for disposable items + this.quantity = 1; + this.maxQuantity = this.thisItem.quantity; } else { - this.quantity = 1; // For non-disposable items, quantity is always 1 - this.maxQuantity = null; // Clear maxQuantity for non-disposable items + this.quantity = 1; + this.maxQuantity = null; } - console.log('last store'+this.thisItem.lastStore); - + console.log('last store' + this.thisItem.lastStore); this.itemlateststatus = this.thisItem.latestStatus ? this.thisItem.latestStatus : this.thisItem.toOther; - this.itemassignedtouser = (this.thisItem.toUser === this.currentUser.id || this.thisItem.lastUser === this.currentUser.id ) && this.thisItem.lastStore === this.currentUser.store ? true : false; - // console.log(this.thisItem); - // console.log(this.itemassignedtouser); - console.log(this.thisItem.lastStore); - console.log( this.thisItem.lastStore == this.currentUser.store? true : false); - console.log(this.thisItem.toUser == this.currentUser.id? true : false); - console.log( this.thisItem.lastUser == this.currentUser.id ? true : false); - console.log(((this.thisItem.toUser == this.currentUser.id) || ( this.thisItem.lastUser == this.currentUser.id)) ? true : false); - console.log('currentuser store'+this.currentUser.store); + this.itemassignedtouser = (this.thisItem.toUser === this.currentUser.id || this.thisItem.lastUser === this.currentUser.id) && this.thisItem.lastStore === this.currentUser.store ? true : false; + console.log(this.thisItem.lastStore); + console.log(this.thisItem.lastStore == this.currentUser.store ? true : false); + console.log(this.thisItem.toUser == this.currentUser.id ? true : false); + console.log(this.thisItem.lastUser == this.currentUser.id ? true : false); + console.log(((this.thisItem.toUser == this.currentUser.id) || (this.thisItem.lastUser == this.currentUser.id)) ? true : false); + console.log('currentuser store' + this.currentUser.store); } else { - console.error('Failed to fetch item information'); - this.responseMessage = await response.text(); + // If the response is not OK (e.g., 404 Not Found) + const errorData = await response.json(); // Assuming your API sends a JSON error object + // Show an alert message to the user + alert('QR Scan Error: ' + errorData.message); + this.resetScanner(); // Optionally reset the scanner on error } } catch (error) { console.error('Error fetching item information:', error); + // Show an alert message for network or other unhandled errors + alert('An unexpected error occurred: ' + error.message); + this.resetScanner(); // Optionally reset the scanner on unexpected error } }, async fetchSuppliers() { @@ -1030,27 +1032,19 @@ } const now = new Date(); - - try { - // First Movement: Cancellation Record (EXACTLY AS YOU HAVE IT) - const cancellationMovementData = { - ItemId: this.thisItem.itemID, - ToStore: this.currentUser.store, - ToUser: this.currentUser.id, - ToOther: null, - sendDate: new Date(now.getTime() + 8 * 60 * 60 * 1000).toISOString(), - Action: 'Stock Out', - Quantity: this.thisItem.quantity, - Remark: `Item has been cancelled: ${this.cancelRemark}`, - ConsignmentNote: this.thisItem.consignmentNote, - LastUser: this.currentUser.id, - LastStore: this.currentUser.store, - LatestStatus: 'Ready To Deploy', - receiveDate: null, - MovementComplete: true, - }; - // Second Movement: Re-registration/Re-stock Record (EXACTLY AS YOU HAVE IT) + try { + // --- REMOVED: First Movement: Cancellation Record (cancellationMovementData) --- + + // Fetch the current item movement details to get the existing Remark + const originalMovementDetailsResponse = await fetch(`/InvMainAPI/GetItemMovementById?id=${this.thisItem.movementId}`); // Assuming you have an API endpoint to get a single item movement by ID + if (!originalMovementDetailsResponse.ok) { + throw new Error('Failed to retrieve original item movement details.'); + } + const originalMovementDetails = await originalMovementDetailsResponse.json(); + const currentRemark = originalMovementDetails.remark || ''; // Get existing remark, default to empty string if null + + // Second Movement: Re-registration/Re-stock Record const registrationMovementData = { ItemId: this.thisItem.itemID, ToStore: this.currentUser.store, @@ -1058,7 +1052,7 @@ ToOther: null, sendDate: null, Action: 'Register', - Quantity: this.thisItem.quantity, + Quantity: this.thisItem.movementQuantity, // Use the movement quantity, not item quantity Remark: null, ConsignmentNote: null, LastUser: this.currentUser.id, @@ -1076,24 +1070,17 @@ Id: this.thisItem.movementId, MovementComplete: true, LatestStatus: 'Cancelled', - Remark: `Movement cancelled: ${this.cancelRemark}` + // Append the cancellation remark to the current remark + Remark: `${currentRemark.trim()}${currentRemark.trim() ? ' / ' : ''}Movement cancelled: ${this.cancelRemark}` }), }); - + if (!updateOriginalResponse.ok) { throw new Error('Failed to update original movement as cancelled.'); } - // Send the first movement (cancellation) - const response1 = await fetch('/InvMainAPI/AddItemMovement', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(cancellationMovementData), - }); - - if (!response1.ok) { - throw new Error('Failed to record cancellation movement.'); - } + // --- REMOVED: Send the first movement (cancellation) --- + // const response1 = await fetch('/InvMainAPI/AddItemMovement', { ... }); // Send the second movement (registration/re-stock) const response2 = await fetch('/InvMainAPI/AddItemMovement', { @@ -1101,7 +1088,7 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(registrationMovementData), }); - + if (!response2.ok) { throw new Error('Failed to record re-registration movement.'); } @@ -1112,11 +1099,11 @@ headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ItemId: this.thisItem.itemID, - Quantity: this.thisItem.quantity, // The quantity to add back - MovementId: this.thisItem.movementId // Add the movement ID + MovementId: this.thisItem.movementId // Pass the movement ID to find the exact quantity + // Don't pass Quantity here - API will get it from the movement record }), }); - + if (!updateItemResponse.ok) { throw new Error('Failed to update item quantity.'); } diff --git a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml index decc45f..c995fb1 100644 --- a/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml +++ b/Areas/OTcalculate/Views/Overtime/OtRecords.cshtml @@ -452,7 +452,7 @@ handleFileUpload(event) { this.selectedFile = event.target.files[0]; }, - async submitOvertime() { + async submitOvertime() { if (!this.selectedFile) { alert("Please select a file to upload."); return; @@ -464,13 +464,12 @@ formData.append('File', this.selectedFile); try { - const res = await axios.post('/OvertimeAPI/SubmitOvertime', formData, { - headers: { - 'Content-Type': 'multipart/form-data' - } + const response = await fetch('/OvertimeAPI/SubmitOvertime', { + method: 'POST', + body: formData }); - if (res.status === 200) { + if (response.ok) { // Check if the response status is 2xx alert('Overtime submitted successfully!'); const modalEl = document.getElementById('submitModal'); @@ -480,10 +479,12 @@ await this.getSubmissionStatus(); } else { - alert('Submission failed.'); + const errorText = await response.text(); // Get error message from response body + alert(`Submission failed: ${errorText}`); + console.error('Submission failed:', response.status, errorText); } } catch (error) { - console.error(error); + console.error("Error during submission:", error); alert('An error occurred during submission.'); } } diff --git a/Controllers/API/Inventory/InvMainAPI.cs b/Controllers/API/Inventory/InvMainAPI.cs index 830a8c2..0cc7f6e 100644 --- a/Controllers/API/Inventory/InvMainAPI.cs +++ b/Controllers/API/Inventory/InvMainAPI.cs @@ -497,7 +497,9 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory try { var product = await _centralDbContext.Products.FirstOrDefaultAsync(p => p.ProductId == item.ProductId) ?? throw new Exception("Product not found"); - var inventoryMaster = await _centralDbContext.InventoryMasters.Include("User").FirstOrDefaultAsync(i => i.UserId == item.CreatedByUserId) ?? new InventoryMasterModel { UserId = item.CreatedByUserId }; + var inventoryMaster = await _centralDbContext.InventoryMasters + .Include("User") + .FirstOrDefaultAsync(i => i.UserId == item.CreatedByUserId) ?? new InventoryMasterModel { UserId = item.CreatedByUserId }; if (product.Category == "Disposable") { @@ -514,6 +516,8 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory item.Quantity = 1; // Force quantity to 1 for Assets/Parts if it's not already } + item.CreateDate = DateTime.Now; + _centralDbContext.Items.Add(item); _centralDbContext.Products.Update(product); // Update the product quantity @@ -580,6 +584,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory savedItem.EndWDate, savedItem.InvoiceDate, savedItem.PartNumber, + CreateDate = savedItem.CreateDate.ToString("dd/MM/yyyy HH:mm:ss") }; return Json(updatedItem); } @@ -1144,6 +1149,25 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory } } + [HttpGet("GetItemMovementById")] + public async Task GetItemMovementById(int id) + { + try + { + var itemMovement = await _centralDbContext.ItemMovements.FindAsync(id); + + if (itemMovement == null) + { + return NotFound("Item movement record not found."); + } + + return Ok(itemMovement); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + } #endregion ItemMovement #region ItemMovementUser diff --git a/wwwroot/Media/Overtime/44e6cc64-fccf-4ca4-87cf-7c0e1b0b8a7b_MMTE006_20250529_052016.pdf b/wwwroot/Media/Overtime/44e6cc64-fccf-4ca4-87cf-7c0e1b0b8a7b_MMTE006_20250529_052016.pdf new file mode 100644 index 0000000..cc4dc2b Binary files /dev/null and b/wwwroot/Media/Overtime/44e6cc64-fccf-4ca4-87cf-7c0e1b0b8a7b_MMTE006_20250529_052016.pdf differ diff --git a/wwwroot/Media/Overtime/d210bb57-515a-496a-83db-1733ef75f58e_MMTE006_20250529_052016.pdf b/wwwroot/Media/Overtime/d210bb57-515a-496a-83db-1733ef75f58e_MMTE006_20250529_052016.pdf new file mode 100644 index 0000000..cc4dc2b Binary files /dev/null and b/wwwroot/Media/Overtime/d210bb57-515a-496a-83db-1733ef75f58e_MMTE006_20250529_052016.pdf differ