779 lines
36 KiB
C#
779 lines
36 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Text.RegularExpressions;
|
|
using eSPJ.Models;
|
|
using eSPJ.Services;
|
|
|
|
namespace eSPJ.Controllers.SpjDriverUpstController
|
|
{
|
|
[Route("upst/detail-penjemputan")]
|
|
public class DetailPenjemputanController : Controller
|
|
{
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly DetailPenjemputanService _detailService;
|
|
private readonly ILogger<DetailPenjemputanController> _logger;
|
|
private readonly IWebHostEnvironment _env;
|
|
|
|
public DetailPenjemputanController(
|
|
IHttpClientFactory httpClientFactory,
|
|
IConfiguration configuration,
|
|
DetailPenjemputanService detailService,
|
|
ILogger<DetailPenjemputanController> logger,
|
|
IWebHostEnvironment env)
|
|
{
|
|
_httpClientFactory = httpClientFactory;
|
|
_configuration = configuration;
|
|
_detailService = detailService;
|
|
_logger = logger;
|
|
_env = env;
|
|
}
|
|
|
|
private static string ResolveDraftKey(string? draftKey, string? sessionKey, string? spjDetailId = null, string? lokasiAngkutId = null)
|
|
{
|
|
var rawKey = !string.IsNullOrWhiteSpace(draftKey)
|
|
? draftKey
|
|
: !string.IsNullOrWhiteSpace(sessionKey)
|
|
? sessionKey
|
|
: $"non-tps-{spjDetailId}-{lokasiAngkutId}";
|
|
|
|
return string.Concat((rawKey ?? string.Empty).Where(c => char.IsLetterOrDigit(c) || c == '-' || c == '_'));
|
|
}
|
|
|
|
private string GetUploadDirectory(string dateFolder)
|
|
{
|
|
var uploadDir = Path.Combine(_env.ContentRootPath, "uploads", "penjemputan", dateFolder);
|
|
Directory.CreateDirectory(uploadDir);
|
|
return uploadDir;
|
|
}
|
|
|
|
private static string BuildUploadUrl(string dateFolder, string fileName)
|
|
{
|
|
return $"/uploads/penjemputan/{dateFolder}/{fileName}";
|
|
}
|
|
|
|
[HttpGet("")]
|
|
public IActionResult Index()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml");
|
|
}
|
|
[HttpGet("tanpa-tps")]
|
|
public IActionResult TanpaTps()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml");
|
|
}
|
|
|
|
[HttpGet("batal")]
|
|
public IActionResult Batal()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Batal.cshtml");
|
|
}
|
|
|
|
[HttpGet("detail-batal")]
|
|
public IActionResult DetailBatal()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailBatal.cshtml");
|
|
}
|
|
|
|
[HttpGet("detail-selesai")]
|
|
public IActionResult DetailSelesai()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailSelesai.cshtml");
|
|
}
|
|
|
|
[HttpGet("detail-selesai-tanpa-tps")]
|
|
public IActionResult DetailSelesaiTanpaTps()
|
|
{
|
|
return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailSelesaiTanpaTps.cshtml");
|
|
}
|
|
|
|
[HttpPost("")]
|
|
[ValidateAntiForgeryToken]
|
|
public async Task<IActionResult> Submit([FromForm] DetailPenjemputanRequest request)
|
|
{
|
|
try
|
|
{
|
|
var result = await _detailService.SubmitPenjemputanAsync(request);
|
|
|
|
if (result.Success)
|
|
{
|
|
TempData["Success"] = result.Message;
|
|
}
|
|
else
|
|
{
|
|
TempData["Error"] = result.Message;
|
|
}
|
|
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error submitting penjemputan data");
|
|
TempData["Error"] = "Terjadi kesalahan saat menyimpan data.";
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
}
|
|
|
|
[HttpPost("save-draft-non-tps")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> SaveDraftNonTps([FromBody] DraftSaveRequest request)
|
|
{
|
|
if (request == null)
|
|
return BadRequest(new DraftSaveResponse { Success = false, Message = "Request tidak valid." });
|
|
|
|
request.DraftKey = ResolveDraftKey(request.DraftKey, request.SessionKey, request.SpjDetailId, request.LokasiAngkutId);
|
|
request.SessionKey = request.DraftKey;
|
|
if (string.IsNullOrWhiteSpace(request.DraftKey))
|
|
return BadRequest(new DraftSaveResponse { Success = false, Message = "Draft key tidak valid." });
|
|
|
|
var result = await _detailService.SaveDraftNonTpsAsync(request);
|
|
return result.Success ? Ok(result) : StatusCode(500, result);
|
|
}
|
|
|
|
[HttpGet("load-draft-non-tps")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> LoadDraftNonTps([FromQuery] string? draftKey = null, [FromQuery] string? sessionKey = null)
|
|
{
|
|
var key = ResolveDraftKey(draftKey, sessionKey);
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
return Ok(new DraftLoadResponse { Success = true, HasDraft = false, Message = "Draft key kosong." });
|
|
var result = await _detailService.LoadDraftNonTpsAsync(key);
|
|
return Ok(result);
|
|
}
|
|
|
|
[HttpDelete("delete-draft-non-tps")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> DeleteDraftNonTps([FromQuery] string? draftKey = null, [FromQuery] string? sessionKey = null)
|
|
{
|
|
var key = ResolveDraftKey(draftKey, sessionKey);
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
return Ok(new { success = false });
|
|
var ok = await _detailService.DeleteDraftNonTpsAsync(key);
|
|
return Ok(new { success = ok });
|
|
}
|
|
|
|
[HttpPost("save-draft")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> SaveDraft([FromBody] DraftSaveRequest request)
|
|
{
|
|
if (request == null)
|
|
return BadRequest(new DraftSaveResponse { Success = false, Message = "Request tidak valid." });
|
|
|
|
request.DraftKey = ResolveDraftKey(request.DraftKey, request.SessionKey, request.SpjDetailId, request.LokasiAngkutId);
|
|
request.SessionKey = request.DraftKey;
|
|
if (string.IsNullOrWhiteSpace(request.DraftKey))
|
|
return BadRequest(new DraftSaveResponse { Success = false, Message = "Draft key tidak valid." });
|
|
|
|
var result = await _detailService.SaveDraftTpsAsync(request);
|
|
return result.Success ? Ok(result) : StatusCode(500, result);
|
|
}
|
|
|
|
[HttpGet("load-draft")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> LoadDraft([FromQuery] string? draftKey = null, [FromQuery] string? sessionKey = null)
|
|
{
|
|
var key = ResolveDraftKey(draftKey, sessionKey);
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
return Ok(new DraftLoadResponse { Success = true, HasDraft = false, Message = "Draft key kosong." });
|
|
var result = await _detailService.LoadDraftTpsAsync(key);
|
|
return Ok(result);
|
|
}
|
|
|
|
[HttpDelete("delete-draft")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> DeleteDraft([FromQuery] string? draftKey = null, [FromQuery] string? sessionKey = null)
|
|
{
|
|
var key = ResolveDraftKey(draftKey, sessionKey);
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
return Ok(new { success = false });
|
|
var ok = await _detailService.DeleteDraftTpsAsync(key);
|
|
return Ok(new { success = ok });
|
|
}
|
|
|
|
|
|
[HttpPost("upload-foto-kedatangan-non-tps")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> UploadFotoKedatanganNonTps(
|
|
[FromForm] List<IFormFile>? FotoKedatangan,
|
|
[FromForm] string? DraftKey,
|
|
[FromForm] string? SessionKey,
|
|
[FromForm] string? SpjDetailId,
|
|
[FromForm] string? LokasiAngkutId,
|
|
[FromForm] string? WaktuKedatangan,
|
|
[FromForm] string? Latitude,
|
|
[FromForm] string? Longitude,
|
|
[FromForm] string? AlamatJalan)
|
|
{
|
|
if (FotoKedatangan == null || FotoKedatangan.Count == 0)
|
|
return BadRequest(new { success = false, message = "Tidak ada foto." });
|
|
|
|
var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
|
|
var uploadDir = GetUploadDirectory(dateFolder);
|
|
|
|
var fileNames = new List<string>();
|
|
foreach (var file in FotoKedatangan)
|
|
{
|
|
if (file.Length == 0) continue;
|
|
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
|
|
var name = $"kedatangan_{Guid.NewGuid()}{ext}";
|
|
var path = Path.Combine(uploadDir, name);
|
|
await using var stream = new FileStream(path, FileMode.Create);
|
|
await file.CopyToAsync(stream);
|
|
fileNames.Add(name);
|
|
}
|
|
var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n)).ToList();
|
|
|
|
var resolvedDraftKey = ResolveDraftKey(DraftKey, SessionKey, SpjDetailId, LokasiAngkutId);
|
|
if (!string.IsNullOrWhiteSpace(resolvedDraftKey))
|
|
{
|
|
var loadResult = await _detailService.LoadDraftNonTpsAsync(resolvedDraftKey);
|
|
var draft = loadResult.Draft ?? new DraftPenjemputanNonTps { SessionKey = resolvedDraftKey, SpjDetailId = SpjDetailId ?? string.Empty, LokasiAngkutId = LokasiAngkutId ?? string.Empty };
|
|
draft.FotoKedatanganFileNames = fileUrls;
|
|
draft.FotoKedatanganUploaded = true;
|
|
if (!string.IsNullOrWhiteSpace(SpjDetailId)) draft.SpjDetailId = SpjDetailId;
|
|
if (!string.IsNullOrWhiteSpace(LokasiAngkutId)) draft.LokasiAngkutId = LokasiAngkutId;
|
|
if (!string.IsNullOrWhiteSpace(WaktuKedatangan)) draft.WaktuKedatangan = WaktuKedatangan;
|
|
if (!string.IsNullOrWhiteSpace(Latitude)) draft.Latitude = Latitude;
|
|
if (!string.IsNullOrWhiteSpace(Longitude)) draft.Longitude = Longitude;
|
|
if (!string.IsNullOrWhiteSpace(AlamatJalan)) draft.AlamatJalan = AlamatJalan;
|
|
await _detailService.SaveDraftNonTpsAsync(new DraftSaveRequest
|
|
{
|
|
DraftKey = resolvedDraftKey,
|
|
SessionKey = resolvedDraftKey,
|
|
LokasiAngkutId = draft.LokasiAngkutId,
|
|
SpjDetailId = draft.SpjDetailId,
|
|
Latitude = draft.Latitude,
|
|
Longitude = draft.Longitude,
|
|
AlamatJalan = draft.AlamatJalan,
|
|
WaktuKedatangan = draft.WaktuKedatangan,
|
|
FotoKedatanganFileNames = fileUrls,
|
|
FotoKedatanganUploaded = true,
|
|
Timbangan = draft.Timbangan,
|
|
TotalOrganik = draft.TotalOrganik,
|
|
TotalAnorganik = draft.TotalAnorganik,
|
|
TotalResidu = draft.TotalResidu,
|
|
TotalTimbangan = draft.TotalTimbangan,
|
|
FotoPetugasFileNames = draft.FotoPetugasFileNames,
|
|
FotoPetugasUploaded = draft.FotoPetugasUploaded,
|
|
NamaPetugas = draft.NamaPetugas
|
|
});
|
|
}
|
|
|
|
return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto kedatangan berhasil diupload." });
|
|
}
|
|
|
|
[HttpPost("upload-foto-timbangan-non-tps")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> UploadFotoTimbanganNonTps(
|
|
[FromForm] IFormFile? FotoTimbangan,
|
|
[FromForm] string? DraftKey,
|
|
[FromForm] string? SessionKey,
|
|
[FromForm] string? SpjDetailId,
|
|
[FromForm] string? LokasiAngkutId,
|
|
[FromForm] int ItemIndex,
|
|
[FromForm] string? JenisSampah,
|
|
[FromForm] decimal Berat)
|
|
{
|
|
if (FotoTimbangan == null || FotoTimbangan.Length == 0)
|
|
return BadRequest(new { success = false, message = "Tidak ada foto." });
|
|
|
|
var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
|
|
var uploadDir = GetUploadDirectory(dateFolder);
|
|
|
|
var ext = Path.GetExtension(FotoTimbangan.FileName).ToLowerInvariant();
|
|
var jenisSafe = (JenisSampah ?? "residu").ToLowerInvariant();
|
|
var beratStr = Berat.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture).Replace('.', '_');
|
|
var name = $"timbangan{ItemIndex + 1}-{jenisSafe}-{beratStr}{ext}";
|
|
var filePath = Path.Combine(uploadDir, name);
|
|
await using var stream = new FileStream(filePath, FileMode.Create);
|
|
await FotoTimbangan.CopyToAsync(stream);
|
|
var fileUrl = BuildUploadUrl(dateFolder, name);
|
|
|
|
var resolvedDraftKey = ResolveDraftKey(DraftKey, SessionKey, SpjDetailId, LokasiAngkutId);
|
|
if (!string.IsNullOrWhiteSpace(resolvedDraftKey))
|
|
{
|
|
var loadResult = await _detailService.LoadDraftNonTpsAsync(resolvedDraftKey);
|
|
var draft = loadResult.Draft ?? new DraftPenjemputanNonTps { SessionKey = resolvedDraftKey, SpjDetailId = SpjDetailId ?? string.Empty, LokasiAngkutId = LokasiAngkutId ?? string.Empty };
|
|
while (draft.Timbangan.Count <= ItemIndex)
|
|
draft.Timbangan.Add(new DraftTimbanganItem());
|
|
if (!string.IsNullOrWhiteSpace(SpjDetailId)) draft.SpjDetailId = SpjDetailId;
|
|
if (!string.IsNullOrWhiteSpace(LokasiAngkutId)) draft.LokasiAngkutId = LokasiAngkutId;
|
|
draft.Timbangan[ItemIndex] = new DraftTimbanganItem
|
|
{
|
|
FotoFileName = fileUrl,
|
|
JenisSampah = JenisSampah ?? "Residu",
|
|
Berat = Berat,
|
|
Uploaded = true
|
|
};
|
|
await _detailService.SaveDraftNonTpsAsync(new DraftSaveRequest
|
|
{
|
|
DraftKey = resolvedDraftKey,
|
|
SessionKey = resolvedDraftKey,
|
|
LokasiAngkutId = draft.LokasiAngkutId,
|
|
SpjDetailId = draft.SpjDetailId,
|
|
Latitude = draft.Latitude,
|
|
Longitude = draft.Longitude,
|
|
AlamatJalan = draft.AlamatJalan,
|
|
WaktuKedatangan = draft.WaktuKedatangan,
|
|
FotoKedatanganFileNames = draft.FotoKedatanganFileNames,
|
|
FotoKedatanganUploaded = draft.FotoKedatanganUploaded,
|
|
Timbangan = draft.Timbangan,
|
|
TotalOrganik = draft.TotalOrganik,
|
|
TotalAnorganik = draft.TotalAnorganik,
|
|
TotalResidu = draft.TotalResidu,
|
|
TotalTimbangan = draft.TotalTimbangan,
|
|
FotoPetugasFileNames = draft.FotoPetugasFileNames,
|
|
FotoPetugasUploaded = draft.FotoPetugasUploaded,
|
|
NamaPetugas = draft.NamaPetugas
|
|
});
|
|
}
|
|
|
|
return Ok(new { success = true, fileName = name, fileUrl, message = $"Foto timbangan #{ItemIndex + 1} berhasil diupload." });
|
|
}
|
|
|
|
[HttpPost("upload-foto-petugas-non-tps")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> UploadFotoPetugasNonTps(
|
|
[FromForm] List<IFormFile>? FotoPetugas,
|
|
[FromForm] string? DraftKey,
|
|
[FromForm] string? SessionKey,
|
|
[FromForm] string? SpjDetailId,
|
|
[FromForm] string? LokasiAngkutId,
|
|
[FromForm] string? NamaPetugas)
|
|
{
|
|
if (FotoPetugas == null || FotoPetugas.Count == 0)
|
|
return BadRequest(new { success = false, message = "Tidak ada foto." });
|
|
|
|
var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
|
|
var uploadDir = GetUploadDirectory(dateFolder);
|
|
|
|
var fileNames = new List<string>();
|
|
foreach (var file in FotoPetugas)
|
|
{
|
|
if (file.Length == 0) continue;
|
|
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
|
|
var name = $"petugas_{Guid.NewGuid()}{ext}";
|
|
var path = Path.Combine(uploadDir, name);
|
|
await using var stream = new FileStream(path, FileMode.Create);
|
|
await file.CopyToAsync(stream);
|
|
fileNames.Add(name);
|
|
}
|
|
var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n)).ToList();
|
|
|
|
var resolvedDraftKey = ResolveDraftKey(DraftKey, SessionKey, SpjDetailId, LokasiAngkutId);
|
|
if (!string.IsNullOrWhiteSpace(resolvedDraftKey))
|
|
{
|
|
var loadResult = await _detailService.LoadDraftNonTpsAsync(resolvedDraftKey);
|
|
var draft = loadResult.Draft ?? new DraftPenjemputanNonTps { SessionKey = resolvedDraftKey, SpjDetailId = SpjDetailId ?? string.Empty, LokasiAngkutId = LokasiAngkutId ?? string.Empty };
|
|
draft.FotoPetugasFileNames = fileUrls;
|
|
draft.FotoPetugasUploaded = true;
|
|
if (!string.IsNullOrWhiteSpace(SpjDetailId)) draft.SpjDetailId = SpjDetailId;
|
|
if (!string.IsNullOrWhiteSpace(LokasiAngkutId)) draft.LokasiAngkutId = LokasiAngkutId;
|
|
if (!string.IsNullOrWhiteSpace(NamaPetugas)) draft.NamaPetugas = NamaPetugas;
|
|
await _detailService.SaveDraftNonTpsAsync(new DraftSaveRequest
|
|
{
|
|
DraftKey = resolvedDraftKey,
|
|
SessionKey = resolvedDraftKey,
|
|
LokasiAngkutId = draft.LokasiAngkutId,
|
|
SpjDetailId = draft.SpjDetailId,
|
|
Latitude = draft.Latitude,
|
|
Longitude = draft.Longitude,
|
|
AlamatJalan = draft.AlamatJalan,
|
|
WaktuKedatangan = draft.WaktuKedatangan,
|
|
FotoKedatanganFileNames = draft.FotoKedatanganFileNames,
|
|
FotoKedatanganUploaded = draft.FotoKedatanganUploaded,
|
|
Timbangan = draft.Timbangan,
|
|
TotalOrganik = draft.TotalOrganik,
|
|
TotalAnorganik = draft.TotalAnorganik,
|
|
TotalResidu = draft.TotalResidu,
|
|
TotalTimbangan = draft.TotalTimbangan,
|
|
FotoPetugasFileNames = fileUrls,
|
|
FotoPetugasUploaded = true,
|
|
NamaPetugas = draft.NamaPetugas
|
|
});
|
|
}
|
|
|
|
return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto petugas berhasil diupload." });
|
|
}
|
|
|
|
[HttpPost("upload-foto-kedatangan")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> UploadFotoKedatangan(
|
|
[FromForm] List<IFormFile>? FotoKedatangan,
|
|
[FromForm] string? DraftKey,
|
|
[FromForm] string? SessionKey,
|
|
[FromForm] string? SpjDetailId,
|
|
[FromForm] string? LokasiAngkutId,
|
|
[FromForm] string? WaktuKedatangan,
|
|
[FromForm] string? Latitude,
|
|
[FromForm] string? Longitude,
|
|
[FromForm] string? AlamatJalan)
|
|
{
|
|
if (FotoKedatangan == null || FotoKedatangan.Count == 0)
|
|
return BadRequest(new { success = false, message = "Tidak ada foto." });
|
|
|
|
var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
|
|
var uploadDir = GetUploadDirectory(dateFolder);
|
|
|
|
var fileNames = new List<string>();
|
|
foreach (var file in FotoKedatangan)
|
|
{
|
|
if (file.Length == 0) continue;
|
|
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
|
|
var name = $"kedatangan_{Guid.NewGuid()}{ext}";
|
|
var path = Path.Combine(uploadDir, name);
|
|
await using var stream = new FileStream(path, FileMode.Create);
|
|
await file.CopyToAsync(stream);
|
|
fileNames.Add(name);
|
|
}
|
|
var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n)).ToList();
|
|
|
|
var resolvedDraftKeyTps = ResolveDraftKey(DraftKey, SessionKey, SpjDetailId, LokasiAngkutId);
|
|
if (!string.IsNullOrWhiteSpace(resolvedDraftKeyTps))
|
|
{
|
|
var loadResult = await _detailService.LoadDraftTpsAsync(resolvedDraftKeyTps);
|
|
var draft = loadResult.Draft ?? new DraftPenjemputanNonTps { SessionKey = resolvedDraftKeyTps, SpjDetailId = SpjDetailId ?? string.Empty, LokasiAngkutId = LokasiAngkutId ?? string.Empty };
|
|
draft.FotoKedatanganFileNames = fileUrls;
|
|
draft.FotoKedatanganUploaded = true;
|
|
if (!string.IsNullOrWhiteSpace(SpjDetailId)) draft.SpjDetailId = SpjDetailId;
|
|
if (!string.IsNullOrWhiteSpace(LokasiAngkutId)) draft.LokasiAngkutId = LokasiAngkutId;
|
|
if (!string.IsNullOrWhiteSpace(WaktuKedatangan)) draft.WaktuKedatangan = WaktuKedatangan;
|
|
if (!string.IsNullOrWhiteSpace(Latitude)) draft.Latitude = Latitude;
|
|
if (!string.IsNullOrWhiteSpace(Longitude)) draft.Longitude = Longitude;
|
|
if (!string.IsNullOrWhiteSpace(AlamatJalan)) draft.AlamatJalan = AlamatJalan;
|
|
await _detailService.SaveDraftTpsAsync(new DraftSaveRequest
|
|
{
|
|
DraftKey = resolvedDraftKeyTps,
|
|
SessionKey = resolvedDraftKeyTps,
|
|
LokasiAngkutId = draft.LokasiAngkutId,
|
|
SpjDetailId = draft.SpjDetailId,
|
|
Latitude = draft.Latitude,
|
|
Longitude = draft.Longitude,
|
|
AlamatJalan = draft.AlamatJalan,
|
|
WaktuKedatangan = draft.WaktuKedatangan,
|
|
FotoKedatanganFileNames = fileUrls,
|
|
FotoKedatanganUploaded = true,
|
|
Timbangan = draft.Timbangan,
|
|
TotalOrganik = draft.TotalOrganik,
|
|
TotalAnorganik = draft.TotalAnorganik,
|
|
TotalResidu = draft.TotalResidu,
|
|
TotalTimbangan = draft.TotalTimbangan,
|
|
FotoPetugasFileNames = draft.FotoPetugasFileNames,
|
|
FotoPetugasUploaded = draft.FotoPetugasUploaded,
|
|
NamaPetugas = draft.NamaPetugas
|
|
});
|
|
}
|
|
|
|
return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto kedatangan berhasil diupload." });
|
|
}
|
|
|
|
[HttpPost("upload-foto-timbangan")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> UploadFotoTimbangan(
|
|
[FromForm] IFormFile? FotoTimbangan,
|
|
[FromForm] string? DraftKey,
|
|
[FromForm] string? SessionKey,
|
|
[FromForm] string? SpjDetailId,
|
|
[FromForm] string? LokasiAngkutId,
|
|
[FromForm] int ItemIndex,
|
|
[FromForm] string? JenisSampah,
|
|
[FromForm] decimal Berat)
|
|
{
|
|
if (FotoTimbangan == null || FotoTimbangan.Length == 0)
|
|
return BadRequest(new { success = false, message = "Tidak ada foto." });
|
|
|
|
var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
|
|
var uploadDir = GetUploadDirectory(dateFolder);
|
|
|
|
var ext = Path.GetExtension(FotoTimbangan.FileName).ToLowerInvariant();
|
|
var jenisSafe = (JenisSampah ?? "residu").ToLowerInvariant();
|
|
var beratStr = Berat.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture).Replace('.', '_');
|
|
var name = $"timbangan{ItemIndex + 1}-{jenisSafe}-{beratStr}{ext}";
|
|
var filePath = Path.Combine(uploadDir, name);
|
|
await using var stream = new FileStream(filePath, FileMode.Create);
|
|
await FotoTimbangan.CopyToAsync(stream);
|
|
var fileUrl = BuildUploadUrl(dateFolder, name);
|
|
|
|
var resolvedDraftKeyTps = ResolveDraftKey(DraftKey, SessionKey, SpjDetailId, LokasiAngkutId);
|
|
if (!string.IsNullOrWhiteSpace(resolvedDraftKeyTps))
|
|
{
|
|
var loadResult = await _detailService.LoadDraftTpsAsync(resolvedDraftKeyTps);
|
|
var draft = loadResult.Draft ?? new DraftPenjemputanNonTps { SessionKey = resolvedDraftKeyTps, SpjDetailId = SpjDetailId ?? string.Empty, LokasiAngkutId = LokasiAngkutId ?? string.Empty };
|
|
while (draft.Timbangan.Count <= ItemIndex)
|
|
draft.Timbangan.Add(new DraftTimbanganItem());
|
|
if (!string.IsNullOrWhiteSpace(SpjDetailId)) draft.SpjDetailId = SpjDetailId;
|
|
if (!string.IsNullOrWhiteSpace(LokasiAngkutId)) draft.LokasiAngkutId = LokasiAngkutId;
|
|
draft.Timbangan[ItemIndex] = new DraftTimbanganItem
|
|
{
|
|
FotoFileName = fileUrl,
|
|
JenisSampah = JenisSampah ?? "Residu",
|
|
Berat = Berat,
|
|
Uploaded = true
|
|
};
|
|
await _detailService.SaveDraftTpsAsync(new DraftSaveRequest
|
|
{
|
|
DraftKey = resolvedDraftKeyTps,
|
|
SessionKey = resolvedDraftKeyTps,
|
|
LokasiAngkutId = draft.LokasiAngkutId,
|
|
SpjDetailId = draft.SpjDetailId,
|
|
Latitude = draft.Latitude,
|
|
Longitude = draft.Longitude,
|
|
AlamatJalan = draft.AlamatJalan,
|
|
WaktuKedatangan = draft.WaktuKedatangan,
|
|
FotoKedatanganFileNames = draft.FotoKedatanganFileNames,
|
|
FotoKedatanganUploaded = draft.FotoKedatanganUploaded,
|
|
Timbangan = draft.Timbangan,
|
|
TotalOrganik = draft.TotalOrganik,
|
|
TotalAnorganik = draft.TotalAnorganik,
|
|
TotalResidu = draft.TotalResidu,
|
|
TotalTimbangan = draft.TotalTimbangan,
|
|
FotoPetugasFileNames = draft.FotoPetugasFileNames,
|
|
FotoPetugasUploaded = draft.FotoPetugasUploaded,
|
|
NamaPetugas = draft.NamaPetugas
|
|
});
|
|
}
|
|
|
|
return Ok(new { success = true, fileName = name, fileUrl, message = $"Foto timbangan #{ItemIndex + 1} berhasil diupload." });
|
|
}
|
|
|
|
[HttpPost("upload-foto-petugas")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> UploadFotoPetugas(
|
|
[FromForm] List<IFormFile>? FotoPetugas,
|
|
[FromForm] string? DraftKey,
|
|
[FromForm] string? SessionKey,
|
|
[FromForm] string? SpjDetailId,
|
|
[FromForm] string? LokasiAngkutId,
|
|
[FromForm] string? NamaPetugas)
|
|
{
|
|
if (FotoPetugas == null || FotoPetugas.Count == 0)
|
|
return BadRequest(new { success = false, message = "Tidak ada foto." });
|
|
|
|
var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
|
|
var uploadDir = GetUploadDirectory(dateFolder);
|
|
|
|
var fileNames = new List<string>();
|
|
foreach (var file in FotoPetugas)
|
|
{
|
|
if (file.Length == 0) continue;
|
|
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
|
|
var name = $"petugas_{Guid.NewGuid()}{ext}";
|
|
var path = Path.Combine(uploadDir, name);
|
|
await using var stream = new FileStream(path, FileMode.Create);
|
|
await file.CopyToAsync(stream);
|
|
fileNames.Add(name);
|
|
}
|
|
var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n)).ToList();
|
|
|
|
var resolvedDraftKeyTps = ResolveDraftKey(DraftKey, SessionKey, SpjDetailId, LokasiAngkutId);
|
|
if (!string.IsNullOrWhiteSpace(resolvedDraftKeyTps))
|
|
{
|
|
var loadResult = await _detailService.LoadDraftTpsAsync(resolvedDraftKeyTps);
|
|
var draft = loadResult.Draft ?? new DraftPenjemputanNonTps { SessionKey = resolvedDraftKeyTps, SpjDetailId = SpjDetailId ?? string.Empty, LokasiAngkutId = LokasiAngkutId ?? string.Empty };
|
|
draft.FotoPetugasFileNames = fileUrls;
|
|
draft.FotoPetugasUploaded = true;
|
|
if (!string.IsNullOrWhiteSpace(SpjDetailId)) draft.SpjDetailId = SpjDetailId;
|
|
if (!string.IsNullOrWhiteSpace(LokasiAngkutId)) draft.LokasiAngkutId = LokasiAngkutId;
|
|
if (!string.IsNullOrWhiteSpace(NamaPetugas)) draft.NamaPetugas = NamaPetugas;
|
|
await _detailService.SaveDraftTpsAsync(new DraftSaveRequest
|
|
{
|
|
DraftKey = resolvedDraftKeyTps,
|
|
SessionKey = resolvedDraftKeyTps,
|
|
LokasiAngkutId = draft.LokasiAngkutId,
|
|
SpjDetailId = draft.SpjDetailId,
|
|
Latitude = draft.Latitude,
|
|
Longitude = draft.Longitude,
|
|
AlamatJalan = draft.AlamatJalan,
|
|
WaktuKedatangan = draft.WaktuKedatangan,
|
|
FotoKedatanganFileNames = draft.FotoKedatanganFileNames,
|
|
FotoKedatanganUploaded = draft.FotoKedatanganUploaded,
|
|
Timbangan = draft.Timbangan,
|
|
TotalOrganik = draft.TotalOrganik,
|
|
TotalAnorganik = draft.TotalAnorganik,
|
|
TotalResidu = draft.TotalResidu,
|
|
TotalTimbangan = draft.TotalTimbangan,
|
|
FotoPetugasFileNames = fileUrls,
|
|
FotoPetugasUploaded = true,
|
|
NamaPetugas = draft.NamaPetugas
|
|
});
|
|
}
|
|
|
|
return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto petugas berhasil diupload." });
|
|
}
|
|
|
|
|
|
[HttpPost("ocr-timbangan")]
|
|
[IgnoreAntiforgeryToken]
|
|
public async Task<IActionResult> OcrTimbangan(IFormFile? Foto)
|
|
{
|
|
if (Foto == null || Foto.Length == 0)
|
|
{
|
|
return BadRequest(new { success = false, message = "Foto tidak ditemukan." });
|
|
}
|
|
|
|
if (Foto.Length > 5 * 1024 * 1024)
|
|
{
|
|
return BadRequest(new { success = false, message = "Ukuran foto terlalu besar. Maksimal 5MB." });
|
|
}
|
|
|
|
var apiKey = _configuration["OpenRouter:OCRkey"];
|
|
if (string.IsNullOrWhiteSpace(apiKey))
|
|
{
|
|
return StatusCode(500, new { success = false, message = "OpenRouter API key belum diset." });
|
|
}
|
|
|
|
byte[] fileBytes;
|
|
await using (var ms = new MemoryStream())
|
|
{
|
|
await Foto.CopyToAsync(ms);
|
|
fileBytes = ms.ToArray();
|
|
}
|
|
|
|
var mimeType = string.IsNullOrWhiteSpace(Foto.ContentType) ? "image/jpeg" : Foto.ContentType;
|
|
var base64 = Convert.ToBase64String(fileBytes);
|
|
var dataUrl = $"data:{mimeType};base64,{base64}";
|
|
|
|
var payload = new
|
|
{
|
|
// model = "nvidia/nemotron-nano-12b-v2-vl:free",
|
|
model = "google/gemini-2.5-flash-image",
|
|
// model = "google/gemini-2.5-flash-lite",
|
|
// model = "google/gemini-2.5-flash-lite-preview-09-2025",
|
|
temperature = 0,
|
|
messages = new object[]
|
|
{
|
|
new
|
|
{
|
|
role = "user",
|
|
content = new object[]
|
|
{
|
|
new
|
|
{
|
|
type = "text",
|
|
text = @"
|
|
Baca angka berat timbangan digital pada foto.
|
|
|
|
Rules:
|
|
- Abaikan tulisan seperti ZERO, TARE, STABLE, AC, PACK, PCS, KG, ADD, HOLD.
|
|
- Jawab hanya angka dengan format 2 digit desimal pakai titik (contoh: 54.45).
|
|
- Jika tidak terbaca jawab: UNREADABLE
|
|
- Fokus pada angka layar LED merah yang menyala.
|
|
|
|
Saya berikan 3 contoh foto timbangan yang benar:
|
|
Foto 1 = 75.23
|
|
Foto 2 = 79.86
|
|
Foto 3 = 54.45
|
|
|
|
Sekarang baca angka pada foto terakhir."
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = "https://res.cloudinary.com/drejcprhe/image/upload/v1770888384/Notes_-_2026-02-11_08.52.31_wonhbm.jpg" }
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = "https://res.cloudinary.com/drejcprhe/image/upload/v1770888429/Notes_-_2026-02-11_08.52.34_xairzy.jpg" }
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = "https://res.cloudinary.com/drejcprhe/image/upload/v1770888473/ChatGPT_Image_Feb_11_2026_03_00_33_PM_ujhdlw.png" }
|
|
},
|
|
|
|
new
|
|
{
|
|
type = "image_url",
|
|
image_url = new { url = dataUrl }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var json = JsonSerializer.Serialize(payload);
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, "https://openrouter.ai/api/v1/chat/completions");
|
|
request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {apiKey}");
|
|
request.Headers.TryAddWithoutValidation("Accept", "application/json");
|
|
request.Headers.TryAddWithoutValidation("HTTP-Referer", "https://yourdomain.com");
|
|
request.Headers.TryAddWithoutValidation("X-Title", "eSPJ OCR Timbangan");
|
|
|
|
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
|
|
var client = _httpClientFactory.CreateClient();
|
|
using var response = await client.SendAsync(request);
|
|
var responseText = await response.Content.ReadAsStringAsync();
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
return StatusCode((int)response.StatusCode, new
|
|
{
|
|
success = false,
|
|
message = "OpenRouter request gagal.",
|
|
detail = responseText
|
|
});
|
|
}
|
|
|
|
using var doc = JsonDocument.Parse(responseText);
|
|
|
|
var content = doc.RootElement
|
|
.GetProperty("choices")[0]
|
|
.GetProperty("message")
|
|
.GetProperty("content")
|
|
.GetString() ?? "";
|
|
|
|
content = content.Trim();
|
|
|
|
if (content.Contains("UNREADABLE", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Ok(new
|
|
{
|
|
success = false,
|
|
message = "Angka tidak terbaca.",
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
// cari format angka 2 desimal
|
|
var match = Regex.Match(content, @"-?\d{1,5}([.,]\d{2})");
|
|
|
|
if (!match.Success)
|
|
{
|
|
return Ok(new
|
|
{
|
|
success = false,
|
|
message = "AI tidak menemukan angka valid.",
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
var normalized = match.Value.Replace(',', '.');
|
|
|
|
if (!decimal.TryParse(normalized, NumberStyles.Any, CultureInfo.InvariantCulture, out var weight))
|
|
{
|
|
return Ok(new
|
|
{
|
|
success = false,
|
|
message = "Format angka AI tidak valid.",
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
weight = weight.ToString("0.00", CultureInfo.InvariantCulture),
|
|
raw = content
|
|
});
|
|
}
|
|
|
|
}
|
|
}
|