updated
This commit is contained in:
parent
1400532680
commit
695af0f339
@ -27,7 +27,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Models
|
|||||||
|
|
||||||
public string OtDescription { get; set; }
|
public string OtDescription { get; set; }
|
||||||
public string OtDays { get; set; }
|
public string OtDays { get; set; }
|
||||||
public required string PDFBase64 { get; set; }
|
public string FilePath { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public int UserId { get; set; }
|
public int UserId { get; set; }
|
||||||
|
|||||||
21
Areas/OTcalculate/Models/OvertimeRequestDto.cs
Normal file
21
Areas/OTcalculate/Models/OvertimeRequestDto.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
namespace PSTW_CentralSystem.Areas.OTcalculate.Models
|
||||||
|
{
|
||||||
|
public class OvertimeRequestDto
|
||||||
|
{
|
||||||
|
public DateTime OtDate { get; set; }
|
||||||
|
public string OfficeFrom { get; set; }
|
||||||
|
public string OfficeTo { get; set; }
|
||||||
|
public int? OfficeBreak { get; set; }
|
||||||
|
public string OutsideFrom { get; set; }
|
||||||
|
public string OutsideTo { get; set; }
|
||||||
|
public int? OutsideBreak { get; set; }
|
||||||
|
public int? StationId { get; set; }
|
||||||
|
public string OtDescription { get; set; }
|
||||||
|
public string OtDays { get; set; }
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public IFormFile File { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -96,7 +96,7 @@ namespace PSTW_CentralSystem.Areas.OTcalculate.Services
|
|||||||
AddHeaderCell("Break\n(Outside)", "#edf2f7");
|
AddHeaderCell("Break\n(Outside)", "#edf2f7");
|
||||||
AddHeaderCell("Total OT\nHours", "#fdebd0");
|
AddHeaderCell("Total OT\nHours", "#fdebd0");
|
||||||
AddHeaderCell("Break Hours\n(min)", "#fdebd0");
|
AddHeaderCell("Break Hours\n(min)", "#fdebd0");
|
||||||
AddHeaderCell("Net OT", "#fdebd0");
|
AddHeaderCell("Net OT Hours", "#fdebd0");
|
||||||
if (departmentId == 2)
|
if (departmentId == 2)
|
||||||
AddHeaderCell("Station", "#d0f0ef");
|
AddHeaderCell("Station", "#d0f0ef");
|
||||||
AddHeaderCell("Days", "#e0f7da");
|
AddHeaderCell("Days", "#e0f7da");
|
||||||
|
|||||||
@ -15,43 +15,58 @@
|
|||||||
v-on:input="calculateOTAndBreak">
|
v-on:input="calculateOTAndBreak">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- OFFICE HOURS -->
|
||||||
<h6 class="fw-bold">OFFICE HOURS</h6>
|
<h6 class="fw-bold">OFFICE HOURS</h6>
|
||||||
<div class="row mb-3">
|
<div class="d-flex gap-3 mb-3 align-items-end flex-wrap">
|
||||||
<div class="col-4">
|
<div style="flex: 1;">
|
||||||
<label for="officeFrom">From</label>
|
<label for="officeFrom">From</label>
|
||||||
<input type="time" class="form-control" v-model="editForm.officeFrom"
|
<input type="time" class="form-control" v-model="editForm.officeFrom"
|
||||||
v-on:input="calculateOTAndBreak">
|
v-on:input="calculateOTAndBreak">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div style="flex: 1;">
|
||||||
<label for="officeTo">To</label>
|
<label for="officeTo">To</label>
|
||||||
<input type="time" id="officeTo" class="form-control" v-model="editForm.officeTo"
|
<input type="time" id="officeTo" class="form-control" v-model="editForm.officeTo"
|
||||||
v-on:input="calculateOTAndBreak">
|
v-on:input="calculateOTAndBreak">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div style="flex: 1;">
|
||||||
<label for="officeBreak">Break Hours (Minutes)</label>
|
<label for="officeBreak">Break Hours (Minutes)</label>
|
||||||
<input type="number" id="officeBreak" class="form-control" v-model="editForm.officeBreak"
|
<div class="d-flex">
|
||||||
|
<input type="number" id="officeBreak" class="form-control"
|
||||||
|
v-model="editForm.officeBreak"
|
||||||
v-on:input="calculateOTAndBreak" placeholder="e.g. 30">
|
v-on:input="calculateOTAndBreak" placeholder="e.g. 30">
|
||||||
|
<button class="btn btn-outline-danger ms-2" v-on:click="clearOfficeHours" title="Clear Office Hours">
|
||||||
|
<i class="bi bi-x-circle"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- OUTSIDE OFFICE HOURS -->
|
||||||
<h6 class="fw-bold text-danger">OUTSIDE OFFICE HOURS</h6>
|
<h6 class="fw-bold text-danger">OUTSIDE OFFICE HOURS</h6>
|
||||||
<div class="row mb-2">
|
<div class="d-flex gap-3 mb-3 align-items-end flex-wrap">
|
||||||
<div class="col-4">
|
<div style="flex: 1;">
|
||||||
<label for="outsideFrom">From</label>
|
<label for="outsideFrom">From</label>
|
||||||
<input type="time" id="outsideFrom" class="form-control" v-model="editForm.outsideFrom"
|
<input type="time" id="outsideFrom" class="form-control" v-model="editForm.outsideFrom"
|
||||||
v-on:input="calculateOTAndBreak">
|
v-on:input="calculateOTAndBreak">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div style="flex: 1;">
|
||||||
<label for="outsideTo">To</label>
|
<label for="outsideTo">To</label>
|
||||||
<input type="time" id="outsideTo" class="form-control" v-model="editForm.outsideTo"
|
<input type="time" id="outsideTo" class="form-control" v-model="editForm.outsideTo"
|
||||||
v-on:input="calculateOTAndBreak">
|
v-on:input="calculateOTAndBreak">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div style="flex: 1;">
|
||||||
<label for="outsideBreak">Break Hours (Minutes)</label>
|
<label for="outsideBreak">Break Hours (Minutes)</label>
|
||||||
<input type="number" id="outsideBreak" class="form-control" v-model="editForm.outsideBreak"
|
<div class="d-flex">
|
||||||
|
<input type="number" id="outsideBreak" class="form-control"
|
||||||
|
v-model="editForm.outsideBreak"
|
||||||
v-on:input="calculateOTAndBreak" placeholder="e.g. 45">
|
v-on:input="calculateOTAndBreak" placeholder="e.g. 45">
|
||||||
|
<button class="btn btn-outline-danger ms-2" v-on:click="clearOutsideHours" title="Clear Outside Hours">
|
||||||
|
<i class="bi bi-x-circle"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-3" v-if="isPSTWAIR">
|
<div class="mb-3" v-if="isPSTWAIR">
|
||||||
<label for="airstationDropdown">Air Station</label>
|
<label for="airstationDropdown">Air Station</label>
|
||||||
@ -104,6 +119,11 @@
|
|||||||
v-on:change="handleFileUpload" ref="fileInput">
|
v-on:change="handleFileUpload" ref="fileInput">
|
||||||
<small class="text-danger">*upload pdf file only</small>
|
<small class="text-danger">*upload pdf file only</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="editForm.filePath" class="mb-2">
|
||||||
|
<a :href="editForm.filePath" target="_blank" class="btn btn-outline-primary btn-sm">
|
||||||
|
View Existing PDF
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mb-3 d-flex flex-column align-items-center">
|
<div class="mb-3 d-flex flex-column align-items-center">
|
||||||
<label for="totalOTHours">Total OT Hours</label>
|
<label for="totalOTHours">Total OT Hours</label>
|
||||||
@ -147,7 +167,8 @@
|
|||||||
stationId: "",
|
stationId: "",
|
||||||
otDescription: "",
|
otDescription: "",
|
||||||
otDays: "",
|
otDays: "",
|
||||||
pdfBase64: "",
|
filePath: "",
|
||||||
|
newFile: null,
|
||||||
userId: null,
|
userId: null,
|
||||||
},
|
},
|
||||||
airstationList: [],
|
airstationList: [],
|
||||||
@ -291,11 +312,7 @@
|
|||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
|
|
||||||
if (file && file.type === "application/pdf") {
|
if (file && file.type === "application/pdf") {
|
||||||
const reader = new FileReader();
|
this.editForm.newFile = file;
|
||||||
reader.onload = () => {
|
|
||||||
this.editForm.pdfBase64 = reader.result.split(',')[1]; // Remove data URI prefix
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
} else {
|
} else {
|
||||||
alert("Please upload a valid PDF file.");
|
alert("Please upload a valid PDF file.");
|
||||||
}
|
}
|
||||||
@ -307,19 +324,44 @@
|
|||||||
},
|
},
|
||||||
async updateRecord() {
|
async updateRecord() {
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const formData = new FormData();
|
||||||
...this.editForm,
|
|
||||||
otDays: this.editForm.selectedDayType, // ensure correct day value
|
formData.append("OvertimeId", this.editForm.overtimeId);
|
||||||
officeFrom: this.formatTime(this.editForm.officeFrom),
|
formData.append("OtDate", this.editForm.otDate);
|
||||||
officeTo: this.formatTime(this.editForm.officeTo),
|
formData.append("StationId", this.editForm.stationId || "");
|
||||||
outsideFrom: this.formatTime(this.editForm.outsideFrom),
|
formData.append("OtDescription", this.editForm.otDescription || "");
|
||||||
outsideTo: this.formatTime(this.editForm.outsideTo)
|
formData.append("OtDays", this.editForm.selectedDayType);
|
||||||
};
|
formData.append("OfficeFrom", this.formatTime(this.editForm.officeFrom) || "");
|
||||||
|
formData.append("OfficeTo", this.formatTime(this.editForm.officeTo) || "");
|
||||||
|
formData.append("OutsideFrom", this.formatTime(this.editForm.outsideFrom) || "");
|
||||||
|
formData.append("OutsideTo", this.formatTime(this.editForm.outsideTo) || "");
|
||||||
|
formData.append("FilePath", this.editForm.filePath || "");
|
||||||
|
|
||||||
|
|
||||||
|
// Conditionally include nullable fields
|
||||||
|
if (this.editForm.outsideFrom) {
|
||||||
|
formData.append("outsideFrom", this.formatTime(this.editForm.outsideFrom));
|
||||||
|
}
|
||||||
|
if (this.editForm.outsideTo) {
|
||||||
|
formData.append("outsideTo", this.formatTime(this.editForm.outsideTo));
|
||||||
|
}
|
||||||
|
|
||||||
|
formData.append("officeBreak", this.editForm.officeBreak || 0);
|
||||||
|
formData.append("outsideBreak", this.editForm.outsideBreak || 0);
|
||||||
|
|
||||||
|
// Required field
|
||||||
|
formData.append("userId", this.currentUser?.id);
|
||||||
|
|
||||||
|
// File handling
|
||||||
|
if (this.editForm.newFile) {
|
||||||
|
formData.append("newFile", this.editForm.newFile);
|
||||||
|
} else {
|
||||||
|
formData.append("filePath", this.editForm.filePath || ""); // must send this
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetch('/OvertimeAPI/UpdateOvertimeRecord', {
|
const res = await fetch('/OvertimeAPI/UpdateOvertimeRecord', {
|
||||||
method: 'PUT',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
body: formData
|
||||||
body: JSON.stringify(payload)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@ -334,9 +376,23 @@
|
|||||||
console.error("Update error:", err);
|
console.error("Update error:", err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
goBack() {
|
goBack() {
|
||||||
window.location.href = "/OTcalculate/Overtime/OtRecords";
|
window.location.href = "/OTcalculate/Overtime/OtRecords";
|
||||||
},
|
},
|
||||||
|
clearOfficeHours() {
|
||||||
|
this.editForm.officeFrom = "";
|
||||||
|
this.editForm.officeTo = "";
|
||||||
|
this.editForm.officeBreak = 0;
|
||||||
|
this.calculateOTAndBreak();
|
||||||
|
},
|
||||||
|
clearOutsideHours() {
|
||||||
|
this.editForm.outsideFrom = "";
|
||||||
|
this.editForm.outsideTo = "";
|
||||||
|
this.editForm.outsideBreak = 0;
|
||||||
|
this.calculateOTAndBreak();
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -100,7 +100,7 @@
|
|||||||
<th class="header-blue" colspan="3">Outside<br><small>(17:30 - 8:30)</small></th>
|
<th class="header-blue" colspan="3">Outside<br><small>(17:30 - 8:30)</small></th>
|
||||||
<th class="header-orange" rowspan="2">Total OT Hours</th>
|
<th class="header-orange" rowspan="2">Total OT Hours</th>
|
||||||
<th class="header-orange" rowspan="2">Break Hours (min)</th>
|
<th class="header-orange" rowspan="2">Break Hours (min)</th>
|
||||||
<th class="header-orange" rowspan="2">Net OT</th>
|
<th class="header-orange" rowspan="2">Net OT Hours</th>
|
||||||
<th class="header-orange" rowspan="2" v-if="isPSTWAIR">Station</th>
|
<th class="header-orange" rowspan="2" v-if="isPSTWAIR">Station</th>
|
||||||
<th class="header-green" rowspan="2">Days</th>
|
<th class="header-green" rowspan="2">Days</th>
|
||||||
<th class="header-blue" rowspan="2">Description</th>
|
<th class="header-blue" rowspan="2">Description</th>
|
||||||
@ -136,10 +136,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span v-if="record.pdfBase64">
|
<span v-if="record.filePath">
|
||||||
<button class="btn btn-light border rounded-circle" title="View PDF" v-on:click="viewPdf(record.pdfBase64)">
|
<a :href="record.filePath" target="_blank" class="btn btn-light border rounded-circle" title="View PDF">
|
||||||
<i class="bi bi-file-earmark-pdf-fill fs-5"></i>
|
<i class="bi bi-file-earmark-pdf-fill fs-5"></i>
|
||||||
</button>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</td>
|
</td>
|
||||||
@ -365,10 +365,7 @@
|
|||||||
alert("An error occurred during submission.");
|
alert("An error occurred during submission.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
viewPdf(base64) {
|
|
||||||
const pdfWindow = window.open("");
|
|
||||||
pdfWindow.document.write(`<iframe width='100%' height='100%' src='data:application/pdf;base64,${base64}'></iframe>`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|||||||
@ -270,33 +270,24 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Sending userId:", this.userId);
|
const formData = new FormData();
|
||||||
|
formData.append("file", this.uploadedFile);
|
||||||
const reader = new FileReader();
|
formData.append("otDate", this.selectedDate);
|
||||||
reader.onload = async (event) => {
|
formData.append("officeFrom", this.officeFrom ? this.formatTime(this.officeFrom) : null);
|
||||||
const base64String = event.target.result.split(',')[1];
|
formData.append("officeTo", this.officeTo ? this.formatTime(this.officeTo) : null);
|
||||||
|
formData.append("officeBreak", this.officeBreak);
|
||||||
const payload = {
|
formData.append("outsideFrom", this.outsideFrom ? this.formatTime(this.outsideFrom) : null);
|
||||||
otDate: this.selectedDate,
|
formData.append("outsideTo", this.outsideTo ? this.formatTime(this.outsideTo) : null);
|
||||||
officeFrom: this.formatTime(this.officeFrom) || null,
|
formData.append("outsideBreak", this.outsideBreak);
|
||||||
officeTo: this.formatTime(this.officeTo) || null,
|
formData.append("stationId", this.isPSTWAIR ? parseInt(this.selectedAirStation) : "");
|
||||||
officeBreak: this.officeBreak || 0,
|
formData.append("otDescription", this.otDescription.trim().split(/\s+/).slice(0, 50).join(' '));
|
||||||
outsideFrom: this.formatTime(this.outsideFrom) || null,
|
formData.append("otDays", this.selectedDayType);
|
||||||
outsideTo: this.formatTime(this.outsideTo) || null,
|
formData.append("userId", this.userId);
|
||||||
outsideBreak: this.outsideBreak || 0,
|
|
||||||
stationId: this.isPSTWAIR ? parseInt(this.selectedAirStation) : null,
|
|
||||||
otDescription: this.otDescription.trim().split(/\s+/).slice(0, 50).join(' '),
|
|
||||||
otDays: this.selectedDayType,
|
|
||||||
pdfBase64: base64String,
|
|
||||||
userId: this.userId,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${window.location.origin}/OvertimeAPI/AddOvertime`, {
|
const response = await fetch(`${window.location.origin}/OvertimeAPI/AddOvertime`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
body: formData,
|
||||||
body: JSON.stringify(payload),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -311,15 +302,8 @@
|
|||||||
console.error("Error adding overtime:", error);
|
console.error("Error adding overtime:", error);
|
||||||
alert("Failed to save overtime. Please check the console for errors.");
|
alert("Failed to save overtime. Please check the console for errors.");
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
reader.onerror = () => {
|
|
||||||
console.error("Error reading file");
|
|
||||||
alert("Error reading the uploaded file.");
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsDataURL(this.uploadedFile);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
clearForm() {
|
clearForm() {
|
||||||
this.selectedDate = "";
|
this.selectedDate = "";
|
||||||
this.officeFrom = "";
|
this.officeFrom = "";
|
||||||
|
|||||||
@ -163,7 +163,7 @@ namespace PSTW_CentralSystem.Controllers.API.Inventory
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
product.QuantityProduct = 0;
|
product.QuantityProduct = 0;
|
||||||
var productImage = product.ImageProduct; // Save image to wwwroot/media/inventory/images | Images name is product.ModelNo | product.ImageProduct is in base64 string
|
var productImage = product.ImageProduct;
|
||||||
if (!string.IsNullOrEmpty(product.ImageProduct))
|
if (!string.IsNullOrEmpty(product.ImageProduct))
|
||||||
{
|
{
|
||||||
var bytes = Convert.FromBase64String(product.ImageProduct);
|
var bytes = Convert.FromBase64String(product.ImageProduct);
|
||||||
|
|||||||
@ -36,14 +36,15 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
private readonly CentralSystemContext _centralDbContext;
|
private readonly CentralSystemContext _centralDbContext;
|
||||||
private readonly UserManager<UserModel> _userManager;
|
private readonly UserManager<UserModel> _userManager;
|
||||||
private readonly OvertimePdfService _pdfService;
|
private readonly OvertimePdfService _pdfService;
|
||||||
|
private readonly IWebHostEnvironment _env;
|
||||||
|
|
||||||
|
public OvertimeAPI(ILogger<OvertimeAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager, OvertimePdfService pdfService, IWebHostEnvironment env)
|
||||||
public OvertimeAPI(ILogger<OvertimeAPI> logger, CentralSystemContext centralDbContext, UserManager<UserModel> userManager, OvertimePdfService pdfService)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_centralDbContext = centralDbContext;
|
_centralDbContext = centralDbContext;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_pdfService = pdfService;
|
_pdfService = pdfService;
|
||||||
|
_env = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Settings
|
#region Settings
|
||||||
@ -390,38 +391,82 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
|
|
||||||
|
|
||||||
[HttpPost("AddOvertime")]
|
[HttpPost("AddOvertime")]
|
||||||
public async Task<IActionResult> AddOvertimeAsync([FromBody] OtRegisterModel model)
|
public async Task<IActionResult> AddOvertimeAsync([FromForm] OvertimeRequestDto request)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("AddOvertimeAsync called.");
|
_logger.LogInformation("AddOvertimeAsync called (with file upload).");
|
||||||
|
|
||||||
if (model == null)
|
if (request == null)
|
||||||
{
|
{
|
||||||
_logger.LogError("Model is null.");
|
_logger.LogError("Request is null.");
|
||||||
return BadRequest("Invalid data.");
|
return BadRequest("Invalid data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"Received model: {System.Text.Json.JsonSerializer.Serialize(model)}");
|
if (request.UserId == 0)
|
||||||
|
|
||||||
if (model.UserId == 0)
|
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No user ID provided.");
|
_logger.LogWarning("No user ID provided.");
|
||||||
return BadRequest("User ID is required.");
|
return BadRequest("User ID is required.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert string time values to TimeSpan before saving
|
// Parse times
|
||||||
model.OfficeFrom = model.GetOfficeFrom();
|
TimeSpan? officeFrom = TimeSpan.TryParse(request.OfficeFrom, out var of) ? of : (TimeSpan?)null;
|
||||||
model.OfficeTo = model.GetOfficeTo();
|
TimeSpan? officeTo = TimeSpan.TryParse(request.OfficeTo, out var ot) ? ot : (TimeSpan?)null;
|
||||||
model.OutsideFrom = model.GetOutsideFrom();
|
TimeSpan? outsideFrom = TimeSpan.TryParse(request.OutsideFrom, out var ofr) ? ofr : (TimeSpan?)null;
|
||||||
model.OutsideTo = model.GetOutsideTo();
|
TimeSpan? outsideTo = TimeSpan.TryParse(request.OutsideTo, out var otr) ? otr : (TimeSpan?)null;
|
||||||
|
|
||||||
_logger.LogInformation($"Time spans parsed successfully.");
|
if ((officeFrom != null && officeTo == null) || (officeFrom == null && officeTo != null))
|
||||||
|
{
|
||||||
|
return BadRequest("Both Office From and To must be filled if one is provided.");
|
||||||
|
}
|
||||||
|
|
||||||
_centralDbContext.Otregisters.Add(model);
|
if ((outsideFrom != null && outsideTo == null) || (outsideFrom == null && outsideTo != null))
|
||||||
|
{
|
||||||
|
return BadRequest("Both Outside From and To must be filled if one is provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save file
|
||||||
|
string pdfPath = null;
|
||||||
|
if (request.File != null && request.File.Length > 0)
|
||||||
|
{
|
||||||
|
var uploadsFolder = Path.Combine(_env.WebRootPath, "Media", "Overtime");
|
||||||
|
|
||||||
|
if (!Directory.Exists(uploadsFolder))
|
||||||
|
Directory.CreateDirectory(uploadsFolder);
|
||||||
|
|
||||||
|
var fileName = $"OT_{Guid.NewGuid()}{Path.GetExtension(request.File.FileName)}";
|
||||||
|
var filePath = Path.Combine(uploadsFolder, fileName);
|
||||||
|
|
||||||
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||||
|
{
|
||||||
|
await request.File.CopyToAsync(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the relative public URL path
|
||||||
|
pdfPath = $"/media/overtime/{fileName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Map to DB model
|
||||||
|
var newRecord = new OtRegisterModel
|
||||||
|
{
|
||||||
|
OtDate = request.OtDate,
|
||||||
|
OfficeFrom = officeFrom,
|
||||||
|
OfficeTo = officeTo,
|
||||||
|
OfficeBreak = request.OfficeBreak,
|
||||||
|
OutsideFrom = outsideFrom,
|
||||||
|
OutsideTo = outsideTo,
|
||||||
|
OutsideBreak = request.OutsideBreak,
|
||||||
|
StationId = request.StationId,
|
||||||
|
OtDescription = request.OtDescription,
|
||||||
|
OtDays = request.OtDays,
|
||||||
|
UserId = request.UserId,
|
||||||
|
FilePath = pdfPath
|
||||||
|
};
|
||||||
|
|
||||||
|
_centralDbContext.Otregisters.Add(newRecord);
|
||||||
await _centralDbContext.SaveChangesAsync();
|
await _centralDbContext.SaveChangesAsync();
|
||||||
|
|
||||||
_logger.LogInformation("Overtime registered successfully.");
|
|
||||||
return Ok(new { message = "Overtime registered successfully." });
|
return Ok(new { message = "Overtime registered successfully." });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -431,6 +476,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Ot Records
|
#region Ot Records
|
||||||
@ -455,7 +501,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
StationName = o.Stations != null ? o.Stations.StationName : "N/A",
|
StationName = o.Stations != null ? o.Stations.StationName : "N/A",
|
||||||
o.OtDescription,
|
o.OtDescription,
|
||||||
o.OtDays,
|
o.OtDays,
|
||||||
o.PDFBase64,
|
o.FilePath,
|
||||||
o.UserId
|
o.UserId
|
||||||
})
|
})
|
||||||
.OrderByDescending(o => o.OtDate)
|
.OrderByDescending(o => o.OtDate)
|
||||||
@ -470,7 +516,6 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpDelete("DeleteOvertimeRecord/{id}")]
|
[HttpDelete("DeleteOvertimeRecord/{id}")]
|
||||||
public IActionResult DeleteOvertimeRecord(int id)
|
public IActionResult DeleteOvertimeRecord(int id)
|
||||||
{
|
{
|
||||||
@ -478,11 +523,31 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
{
|
{
|
||||||
var record = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id);
|
var record = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == id);
|
||||||
if (record == null)
|
if (record == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Overtime record not found for Id: {OvertimeId}", id);
|
||||||
return NotFound("Overtime record not found.");
|
return NotFound("Overtime record not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Delete the file from the server
|
||||||
|
if (!string.IsNullOrEmpty(record.FilePath))
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(_env.WebRootPath, record.FilePath.TrimStart('/')); // Construct full path
|
||||||
|
if (System.IO.File.Exists(filePath))
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(filePath);
|
||||||
|
_logger.LogInformation("File deleted successfully: {FilePath}", filePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("File not found, could not delete: {FilePath}", filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Delete the record from the database
|
||||||
_centralDbContext.Otregisters.Remove(record);
|
_centralDbContext.Otregisters.Remove(record);
|
||||||
_centralDbContext.SaveChanges();
|
_centralDbContext.SaveChanges();
|
||||||
|
|
||||||
|
_logger.LogInformation("Overtime record deleted successfully for Id: {OvertimeId}", id);
|
||||||
return Ok(new { message = "Record deleted successfully." });
|
return Ok(new { message = "Record deleted successfully." });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -513,7 +578,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
if (existingRecord != null)
|
if (existingRecord != null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Updating existing record with OvertimeId: {OvertimeId}", record.OvertimeId);
|
_logger.LogDebug("Updating existing record with OvertimeId: {OvertimeId}", record.OvertimeId);
|
||||||
existingRecord.PDFBase64 = record.PDFBase64;
|
existingRecord.FilePath = record.FilePath;
|
||||||
_centralDbContext.Otregisters.Update(existingRecord);
|
_centralDbContext.Otregisters.Update(existingRecord);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -575,6 +640,7 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Ot Edit
|
||||||
[HttpGet("GetOvertimeRecordById/{id}")]
|
[HttpGet("GetOvertimeRecordById/{id}")]
|
||||||
public async Task<IActionResult> GetOvertimeRecordById(int id)
|
public async Task<IActionResult> GetOvertimeRecordById(int id)
|
||||||
{
|
{
|
||||||
@ -585,16 +651,49 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
return Ok(record);
|
return Ok(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("UpdateOvertimeRecord")]
|
[HttpPost]
|
||||||
public IActionResult UpdateOvertimeRecord([FromBody] OtRegisterModel model)
|
[Route("UpdateOvertimeRecord")]
|
||||||
|
public async Task<IActionResult> UpdateOvertimeRecord([FromForm] OtRegisterModel model, IFormFile? newFile)
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("UpdateOvertimeRecord called. Model: {@Model}", model);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ModelState is invalid. Errors: {@ModelStateErrors}", ModelState.Values.SelectMany(v => v.Errors));
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
var existing = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == model.OvertimeId);
|
var existing = _centralDbContext.Otregisters.FirstOrDefault(o => o.OvertimeId == model.OvertimeId);
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Overtime record not found for Id: {OvertimeId}", model.OvertimeId);
|
||||||
return NotFound("Overtime record not found.");
|
return NotFound("Overtime record not found.");
|
||||||
|
}
|
||||||
|
|
||||||
// Update fields
|
_logger.LogInformation("Existing record found: {@ExistingRecord}", existing);
|
||||||
|
|
||||||
|
string? oldFilePath = existing.FilePath; // Store the old file path
|
||||||
|
|
||||||
|
string? newPdfPath = null; // Initialize the new file path
|
||||||
|
|
||||||
|
// Handle new file upload
|
||||||
|
if (newFile != null && newFile.Length > 0)
|
||||||
|
{
|
||||||
|
var fileName = $"OT_{Guid.NewGuid()}{Path.GetExtension(newFile.FileName)}";
|
||||||
|
var savePath = Path.Combine(_env.WebRootPath, "media", "Overtime", fileName);
|
||||||
|
newPdfPath = $"/media/Overtime/{fileName}";
|
||||||
|
|
||||||
|
using (var stream = new FileStream(savePath, FileMode.Create))
|
||||||
|
{
|
||||||
|
await newFile.CopyToAsync(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
existing.FilePath = newPdfPath; // Update the file path in the database
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update other fields
|
||||||
existing.OtDate = model.OtDate;
|
existing.OtDate = model.OtDate;
|
||||||
existing.OfficeFrom = model.OfficeFrom;
|
existing.OfficeFrom = model.OfficeFrom;
|
||||||
existing.OfficeTo = model.OfficeTo;
|
existing.OfficeTo = model.OfficeTo;
|
||||||
@ -605,19 +704,35 @@ namespace PSTW_CentralSystem.Controllers.API
|
|||||||
existing.StationId = model.StationId;
|
existing.StationId = model.StationId;
|
||||||
existing.OtDescription = model.OtDescription;
|
existing.OtDescription = model.OtDescription;
|
||||||
existing.OtDays = model.OtDays;
|
existing.OtDays = model.OtDays;
|
||||||
existing.PDFBase64 = model.PDFBase64;
|
|
||||||
existing.UserId = model.UserId;
|
existing.UserId = model.UserId;
|
||||||
|
|
||||||
_centralDbContext.SaveChanges();
|
_centralDbContext.SaveChanges();
|
||||||
|
_logger.LogInformation("Overtime record updated successfully for Id: {OvertimeId}", model.OvertimeId);
|
||||||
|
|
||||||
|
// Delete the old file after saving the new one (if a new file was uploaded)
|
||||||
|
if (newFile != null && !string.IsNullOrEmpty(oldFilePath))
|
||||||
|
{
|
||||||
|
var fullOldFilePath = Path.Combine(_env.WebRootPath, oldFilePath.TrimStart('/'));
|
||||||
|
if (System.IO.File.Exists(fullOldFilePath))
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(fullOldFilePath);
|
||||||
|
_logger.LogInformation("Old file deleted successfully: {OldFilePath}", fullOldFilePath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Old file not found, could not delete: {OldFilePath}", fullOldFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(new { message = "Record updated successfully." });
|
return Ok(new { message = "Record updated successfully." });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error updating overtime record.");
|
_logger.LogError(ex, "Error updating overtime record. Stack Trace: {StackTrace}", ex.StackTrace);
|
||||||
return StatusCode(500, "Failed to update record.");
|
return StatusCode(500, "Failed to update record.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
<Folder Include="Areas\Report\Models\" />
|
<Folder Include="Areas\Report\Models\" />
|
||||||
<Folder Include="Controllers\API\Reporting\" />
|
<Folder Include="Controllers\API\Reporting\" />
|
||||||
<Folder Include="Logs\" />
|
<Folder Include="Logs\" />
|
||||||
|
<Folder Include="wwwroot\Media\Overtime\" />
|
||||||
<Folder Include="wwwroot\NewFolder\" />
|
<Folder Include="wwwroot\NewFolder\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user