769 lines
31 KiB
C#
769 lines
31 KiB
C#
using LSFE.Infrastructure.Dto.Maintenance;
|
|
using LSFE.Infrastructure.Dto.Planning;
|
|
using LSFE.Infrastructure.Entities.Maintenance;
|
|
using LSFE.MobApp.Contracts;
|
|
using LSFE.MobApp.Entities.Account;
|
|
using LSFE.MobApp.Entities.Location;
|
|
using LSFE.MobApp.Mapper;
|
|
using LSFE.MobApp.Models;
|
|
using LSFE.MobApp.Services.Authentication;
|
|
using LSFE.MobApp.Services.Coverage;
|
|
using Microsoft.Extensions.Configuration;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LSFE.MobApp.Services
|
|
{
|
|
public class SyncService : ISyncService
|
|
{
|
|
#region Api Client Factory
|
|
private readonly IApiClientFactory _apiClientFactory;
|
|
private readonly IApiClient<Entities.Doctor.Doctor> _doctorApiClient;
|
|
private readonly IApiClient<ProductTransactionDto> _productTransApiClient;
|
|
private readonly IApiClient<InventoryTransaction> _inventoryTransApiClient;
|
|
private readonly IApiClient<MRRawPlansDto> _mrRawPlanApiClient;
|
|
private readonly IApiClient<LoginResponse> _userApiClient;
|
|
#endregion
|
|
|
|
#region Local Repository
|
|
private readonly IRepository<Institution> _institutionRepository;
|
|
private readonly IRepository<Entities.Account.Attendance> _attendanceRepository;
|
|
private readonly IRepository<Entities.Doctor.Doctor> _doctorRepository;
|
|
private readonly IRepository<Entities.Planning.ProductTransaction> _productTransRepository;
|
|
private readonly IRepository<Entities.Planning.InventoryTransaction> _inventoryTransRepository;
|
|
private readonly IRepository<Entities.Planning.MRRawPlan> _mrRawPlanRepository;
|
|
private readonly IRepository<Entities.Account.User> _userRepository;
|
|
#endregion
|
|
|
|
#region Helper
|
|
private readonly IFileDownloadService _fileDownloadService;
|
|
#endregion
|
|
private readonly IConfiguration _configuration;
|
|
private readonly AddressService _addressService;
|
|
public SyncService(
|
|
IApiClientFactory apiClientFactory,
|
|
IRepository<Institution> institutionRepository,
|
|
IRepository<Entities.Doctor.Doctor> doctor,
|
|
IRepository<Entities.Account.Attendance> attendanceRepository,
|
|
IRepository<Entities.Planning.ProductTransaction> productTransRepository,
|
|
IRepository<Entities.Planning.InventoryTransaction> inventoryTransRepository,
|
|
IRepository<Entities.Planning.MRRawPlan> mrRawPlanRepository,
|
|
IRepository<Entities.Account.User> userRepository,
|
|
IConfiguration configuration,
|
|
IFileDownloadService fileDownloadService,
|
|
AddressService addressService)
|
|
{
|
|
#region Api Constructor
|
|
_configuration = configuration;
|
|
_apiClientFactory = apiClientFactory;
|
|
_doctorApiClient = apiClientFactory.CreateClient<Entities.Doctor.Doctor>("DoctorMgmt/GetDoctorCoverageToday");
|
|
_productTransApiClient = apiClientFactory.CreateClient<ProductTransactionDto>("Plan/GetDoctorProductToday");
|
|
_inventoryTransApiClient = apiClientFactory.CreateClient<InventoryTransaction>("Plan/GetDoctorSampleToday");
|
|
_mrRawPlanApiClient = apiClientFactory.CreateClient<MRRawPlansDto>("Plan/GetMRRawPlans");
|
|
_userApiClient = apiClientFactory.CreateClient<LoginResponse>(_configuration["ApiSettings:Login"] ?? "AnonAccount/Login/");
|
|
#endregion
|
|
|
|
#region Local Constructor
|
|
_institutionRepository = institutionRepository;
|
|
_doctorRepository = doctor;
|
|
_productTransRepository = productTransRepository;
|
|
_inventoryTransRepository = inventoryTransRepository;
|
|
_attendanceRepository = attendanceRepository;
|
|
_mrRawPlanRepository = mrRawPlanRepository;
|
|
_userRepository = userRepository;
|
|
#endregion
|
|
|
|
#region Helper
|
|
_fileDownloadService = fileDownloadService;
|
|
_addressService = addressService;
|
|
#endregion
|
|
}
|
|
#region Sync Pull
|
|
public async Task<bool> SyncPullUsersAsync(User users)
|
|
{
|
|
try
|
|
{
|
|
var result = await _userRepository.SaveAsync(users);
|
|
|
|
if (result > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPullMRRawPlansAsync()
|
|
{
|
|
try
|
|
{
|
|
var cred =await UserCred.GetUserCred();
|
|
|
|
if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
|
|
{
|
|
var userResponse = await _userApiClient.CreateAsync(cred);
|
|
cred.Token = userResponse.Token;
|
|
}
|
|
|
|
var serverPlans = await _mrRawPlanApiClient.GetWithQueryAsync(new Dictionary<string, string>
|
|
{
|
|
{ "UserId", cred.UserId },
|
|
{ "UserName", cred.UserName },
|
|
{ "Token", cred.Token }
|
|
});
|
|
|
|
if (serverPlans == null || !serverPlans.Any())
|
|
{
|
|
Debug.WriteLine("No data received from server");
|
|
return false;
|
|
}
|
|
|
|
// Get existing local data
|
|
var localPlans = await _mrRawPlanRepository.GetAllAsync();
|
|
|
|
int newCount = 0;
|
|
int updatedCount = 0;
|
|
|
|
foreach (var serverPlan in serverPlans)
|
|
{
|
|
try
|
|
{
|
|
// Find existing local record by InstitutionId
|
|
var existingLocal = await _mrRawPlanRepository.
|
|
FirstOrDefaultAsync(x => x.MRRawPlanId == serverPlan.MRRawPlanId);
|
|
|
|
if (existingLocal == null)
|
|
{
|
|
// Create new local record
|
|
var newPlan = ApiEntityMapper.MapMRRawPlanFromApi(serverPlan);
|
|
|
|
var result = await _mrRawPlanRepository.SaveAsync(newPlan);
|
|
|
|
if (result > 0)
|
|
{
|
|
newCount++;
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to save: {newPlan.MRRawPlanId}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing record
|
|
ApiEntityMapper.UpdateMRRawPlanFromApi(existingLocal, serverPlan);
|
|
|
|
var result = await _mrRawPlanRepository.UpdateAsync(existingLocal);
|
|
|
|
if (result > 0)
|
|
{
|
|
updatedCount++;
|
|
Debug.WriteLine($"✅ Updated: {existingLocal.MRRawPlanId}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to update: {existingLocal.MRRawPlanId}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception itemEx)
|
|
{
|
|
Debug.WriteLine($"Error processing mrRawPlan {serverPlan.MRRawPlanId}: {itemEx.Message}");
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPullProductsAsync()
|
|
{
|
|
try
|
|
{
|
|
var cred = await UserCred.GetUserCred();
|
|
|
|
if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
|
|
{
|
|
var userResponse = await _userApiClient.CreateAsync(cred);
|
|
cred.Token = userResponse.Token;
|
|
}
|
|
|
|
var serverProducts = await _productTransApiClient.GetWithQueryAsync(new Dictionary<string, string>
|
|
{
|
|
{ "UserId", cred.UserId },
|
|
{ "UserName", cred.UserName },
|
|
{ "Token", cred.Token }
|
|
});
|
|
|
|
if (serverProducts == null || !serverProducts.Any())
|
|
{
|
|
Debug.WriteLine("No data received from server");
|
|
return false;
|
|
}
|
|
|
|
// Get existing local data
|
|
var localProducts = await _productTransRepository.GetAllAsync();
|
|
|
|
int newCount = 0;
|
|
int updatedCount = 0;
|
|
|
|
foreach (var serverProduct in serverProducts)
|
|
{
|
|
try
|
|
{
|
|
// Find existing local record by InstitutionId
|
|
var existingLocal = await _productTransRepository.
|
|
FirstOrDefaultAsync(x => x.ProductTransId == serverProduct.ProductTransId);
|
|
|
|
if (existingLocal == null)
|
|
{
|
|
// Create new local record
|
|
var newProduct = ApiEntityMapper.MapProductTransFromApi(serverProduct);
|
|
|
|
var result = await _productTransRepository.SaveAsync(newProduct);
|
|
|
|
if (result > 0)
|
|
{
|
|
newCount++;
|
|
Debug.WriteLine($"✅ Successfully created: {newProduct.ProductName}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to save: {newProduct.ProductName}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing record
|
|
ApiEntityMapper.UpdateProductTransFromApi(existingLocal, serverProduct);
|
|
|
|
var result = await _productTransRepository.UpdateAsync(existingLocal);
|
|
|
|
if (result > 0)
|
|
{
|
|
updatedCount++;
|
|
Debug.WriteLine($"✅ Updated: {existingLocal.ProductName}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to update: {existingLocal.ProductName}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception itemEx)
|
|
{
|
|
Debug.WriteLine($"Error processing institution {serverProduct.ProductId}: {itemEx.Message}");
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPullSamplesAsync()
|
|
{
|
|
try
|
|
{
|
|
var cred = await UserCred.GetUserCred();
|
|
if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
|
|
{
|
|
var userResponse = await _userApiClient.CreateAsync(cred);
|
|
cred.Token = userResponse.Token;
|
|
}
|
|
|
|
var serverInventoryTrans = await _inventoryTransApiClient.GetWithQueryAsync(new Dictionary<string, string>
|
|
{
|
|
{ "UserId", cred.UserId },
|
|
{ "UserName", cred.UserName },
|
|
{ "Token", cred.Token }
|
|
});
|
|
|
|
if (serverInventoryTrans == null || !serverInventoryTrans.Any())
|
|
{
|
|
Debug.WriteLine("No data received from server");
|
|
return false;
|
|
}
|
|
|
|
// Get existing local data
|
|
var localInventoryTrans = await _inventoryTransRepository.GetAllAsync();
|
|
|
|
int newCount = 0;
|
|
int updatedCount = 0;
|
|
|
|
foreach (var serverInventoryTran in serverInventoryTrans)
|
|
{
|
|
try
|
|
{
|
|
// Find existing local record by InventoryTransId
|
|
var existingLocal = await _inventoryTransRepository.
|
|
FirstOrDefaultAsync(x => x.InventoryTransId == serverInventoryTran.InventoryTransId);
|
|
|
|
if (existingLocal == null)
|
|
{
|
|
// Create new local record
|
|
var newProduct = ApiEntityMapper.MapInventoryTransFromApi(serverInventoryTran);
|
|
|
|
var result = await _inventoryTransRepository.SaveAsync(newProduct);
|
|
|
|
if (result > 0)
|
|
{
|
|
newCount++;
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to save: {newProduct.Description}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing record
|
|
ApiEntityMapper.UpdateInventoryTransFromApi(existingLocal, serverInventoryTran);
|
|
|
|
var result = await _inventoryTransRepository.UpdateAsync(existingLocal);
|
|
|
|
if (result > 0)
|
|
{
|
|
updatedCount++;
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to update: {existingLocal.Description}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception itemEx)
|
|
{
|
|
Debug.WriteLine($"Error processing inventory {serverInventoryTran.InventoryTransId}: {itemEx.Message}");
|
|
}
|
|
}
|
|
|
|
//Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPullInstitutionsAsync()
|
|
{
|
|
try
|
|
{
|
|
// Use the exact API entity name
|
|
var apiClient = _apiClientFactory.CreateClient<Institutions>("Maintenance/institutions");
|
|
var serverInstitutions = await apiClient.GetAllAsync();
|
|
|
|
if (serverInstitutions == null || !serverInstitutions.Any())
|
|
{
|
|
Debug.WriteLine("No data received from server");
|
|
return false;
|
|
}
|
|
|
|
Debug.WriteLine($"Received {serverInstitutions.Count} institutions from API");
|
|
|
|
// Get existing local data
|
|
var localInstitutions = await _institutionRepository.GetAllAsync();
|
|
Debug.WriteLine($"Found {localInstitutions.Count} existing local institutions");
|
|
|
|
int newCount = 0;
|
|
int updatedCount = 0;
|
|
|
|
foreach (var serverInstitution in serverInstitutions)
|
|
{
|
|
try
|
|
{
|
|
// Find existing local record by InstitutionId
|
|
var existingLocal = await _institutionRepository.
|
|
FirstOrDefaultAsync(x => x.InstitutionId == serverInstitution.InstitutionId);
|
|
|
|
if (existingLocal == null)
|
|
{
|
|
// Create new local record
|
|
var newInstitution = ApiEntityMapper.MapInstutionFromApi(serverInstitution);
|
|
|
|
Debug.WriteLine($"Creating new institution: {newInstitution.InstitutionName} (ServerID: {newInstitution.InstitutionId})");
|
|
|
|
var result = await _institutionRepository.SaveAsync(newInstitution);
|
|
|
|
if (result > 0)
|
|
{
|
|
newCount++;
|
|
Debug.WriteLine($"✅ Successfully created: {newInstitution.InstitutionName}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to save: {newInstitution.InstitutionName}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing record
|
|
ApiEntityMapper.UpdateInstutionFromApi(existingLocal, serverInstitution);
|
|
|
|
var result = await _institutionRepository.UpdateAsync(existingLocal);
|
|
|
|
if (result > 0)
|
|
{
|
|
updatedCount++;
|
|
Debug.WriteLine($"✅ Updated: {existingLocal.InstitutionName}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to update: {existingLocal.InstitutionName}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception itemEx)
|
|
{
|
|
Debug.WriteLine($"Error processing institution {serverInstitution.InstitutionId}: {itemEx.Message}");
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPullDoctorsAsync()
|
|
{
|
|
try
|
|
{
|
|
|
|
var cred= await UserCred.GetUserCred();
|
|
if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
|
|
{
|
|
var userResponse = await _userApiClient.CreateAsync(cred);
|
|
cred.Token = userResponse.Token;
|
|
await SecureStorage.SetAsync("auth_token", userResponse.Token ?? "N/A");
|
|
}
|
|
|
|
var serverDoctors = await _doctorApiClient.GetWithQueryAsync(new Dictionary<string, string>
|
|
{
|
|
{ "UserId", cred.UserId },
|
|
{ "UserName", cred.UserName },
|
|
{ "Token", cred.Token }
|
|
});
|
|
|
|
if (serverDoctors == null || !serverDoctors.Any())
|
|
{
|
|
Debug.WriteLine("No data received from server");
|
|
return false;
|
|
}
|
|
|
|
// Get existing local data and update new
|
|
await _doctorRepository.TruncateAsync();
|
|
var localDoctor = await _doctorRepository.GetAllAsync();
|
|
|
|
int newCount = 0;
|
|
int updatedCount = 0;
|
|
|
|
foreach (var serverDoc in serverDoctors)
|
|
{
|
|
try
|
|
{
|
|
// Find existing local record by InstitutionId
|
|
var existingLocal = await _doctorRepository.
|
|
FirstOrDefaultAsync(x => x.DoctorId == serverDoc.DoctorId);
|
|
|
|
if (existingLocal == null)
|
|
{
|
|
// Create new local record
|
|
var newDoctor = ApiEntityMapper.MapDoctorFromApi(serverDoc);
|
|
|
|
Debug.WriteLine($"Creating new institution: {newDoctor.FirstName} (ServerID: {newDoctor.DoctorId})");
|
|
|
|
var result = await _doctorRepository.SaveAsync(newDoctor);
|
|
|
|
if (result > 0)
|
|
{
|
|
newCount++;
|
|
Debug.WriteLine($"✅ Successfully created: {newDoctor.FirstName}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to save: {newDoctor.FirstName}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Update existing record
|
|
ApiEntityMapper.UpdateDoctorFromApi(existingLocal, serverDoc);
|
|
|
|
var result = await _doctorRepository.UpdateAsync(existingLocal);
|
|
|
|
if (result > 0)
|
|
{
|
|
updatedCount++;
|
|
Debug.WriteLine($"✅ Updated: {existingLocal.FirstName}");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"❌ Failed to update: {existingLocal.FirstName}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception itemEx)
|
|
{
|
|
Debug.WriteLine($"Error processing institution {serverDoc.InstitutionId}: {itemEx.Message}");
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine($"Sync completed: {newCount} new, {updatedCount} updated");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPullCoveragePlanTodayAsync()
|
|
{
|
|
await SyncPullDoctorsAsync();
|
|
await SyncPullProductsAsync();
|
|
await SyncPullSamplesAsync();
|
|
await SyncPullMRRawPlansAsync();
|
|
await DownloadAllMaterials();
|
|
return await SyncPullInstitutionsAsync();
|
|
}
|
|
#endregion
|
|
#region Sync Post
|
|
public async Task<bool> SyncPostAttendanceAsync()
|
|
{
|
|
try
|
|
{
|
|
var today = DateTime.Today;
|
|
var localAttendance = await _attendanceRepository.GetAllAsync();
|
|
|
|
var _todayAttendance = localAttendance
|
|
.Where(a => a.TimeIn.HasValue && a.TimeIn.Value.Date == today)
|
|
.OrderByDescending(a => a.TimeIn)
|
|
.FirstOrDefault();
|
|
|
|
// Add null check here
|
|
if (_todayAttendance == null)
|
|
{
|
|
Debug.WriteLine("No attendance record found for today");
|
|
return false;
|
|
}
|
|
var cred = await UserCred.GetUserCred();
|
|
|
|
if (cred.Token == null || cred.Token.Length == 0 || cred.Token == "N/A")
|
|
{
|
|
var userResponse = await _userApiClient.CreateAsync(cred);
|
|
cred.Token = userResponse.Token;
|
|
}
|
|
var apiClient = _apiClientFactory.
|
|
CreateClient<Entities.Account.Attendance>(_configuration["ApiSettings:Endpoints:PostAttendance"]);
|
|
var serverAttendance = await apiClient.CreateAttendanceAsync(_todayAttendance);
|
|
|
|
if (!serverAttendance.Success)
|
|
{
|
|
Debug.WriteLine("No data received from server");
|
|
return false;
|
|
}
|
|
|
|
Debug.WriteLine($"Sync completed");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync error: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
public async Task<bool> SyncPostCoverageTodayAsync(Guid mrRawPlanId)
|
|
{
|
|
try
|
|
{
|
|
// 1. Fetch the local record
|
|
var localActualCoveragePlan = await _mrRawPlanRepository
|
|
.FirstOrDefaultAsync(a => a.MRRawPlanId == mrRawPlanId);
|
|
|
|
// Validate the record exists
|
|
if (localActualCoveragePlan == null)
|
|
{
|
|
Debug.WriteLine($"[SYNC] No coverage plan found for ID: {mrRawPlanId}");
|
|
return false;
|
|
}
|
|
|
|
// Check if already synced (avoid redundant API calls)
|
|
if (localActualCoveragePlan.IsSync)
|
|
{
|
|
Debug.WriteLine($"[SYNC] Coverage plan {mrRawPlanId} already synced. Skipping.");
|
|
return true; // Already synced is a success
|
|
}
|
|
|
|
// 4. Validate required data before sending to API
|
|
if (!ValidateCoveragePlan(localActualCoveragePlan))
|
|
{
|
|
Debug.WriteLine($"[SYNC] Coverage plan {mrRawPlanId} validation failed");
|
|
return false;
|
|
}
|
|
if (localActualCoveragePlan.ForAddressTranslation)
|
|
{
|
|
var (address, isHumanReadable) = await _addressService.
|
|
GetAddressAsync(double.Parse(localActualCoveragePlan.Latitude),
|
|
double.Parse(localActualCoveragePlan.Longitude));
|
|
|
|
localActualCoveragePlan.Location = address;
|
|
}
|
|
// 5. Create API client
|
|
var apiClient = _apiClientFactory
|
|
.CreateClient<Entities.Planning.MRRawPlan>(
|
|
_configuration["ApiSettings:Endpoints:PostActualCoveragePlan"]);
|
|
|
|
Debug.WriteLine($"[SYNC] Sending coverage plan {mrRawPlanId} to server...");
|
|
|
|
// Send to server
|
|
var serverResponse = await apiClient.CreateCoveragePlanAsync(localActualCoveragePlan);
|
|
|
|
// Validate server response
|
|
if (serverResponse == null)
|
|
{
|
|
Debug.WriteLine($"[SYNC] Null response from server for {mrRawPlanId}");
|
|
return false;
|
|
}
|
|
|
|
if (!serverResponse.Success)
|
|
{
|
|
Debug.WriteLine($"[SYNC] Server returned failure for {mrRawPlanId}. " +
|
|
$"Message: {serverResponse.Message ?? "No message"}");
|
|
return false;
|
|
}
|
|
|
|
localActualCoveragePlan.IsSync = true;
|
|
localActualCoveragePlan.LastSyncDate = DateTime.Now;
|
|
|
|
int updateResult = await _mrRawPlanRepository.UpdateAsync(localActualCoveragePlan);
|
|
|
|
if (updateResult <= 0)
|
|
{
|
|
Debug.WriteLine($"[SYNC] Failed to update local IsSync flag for {mrRawPlanId}");
|
|
// Note: Data is on server but local flag update failed
|
|
// Consider how to handle this edge case
|
|
return false;
|
|
}
|
|
|
|
Debug.WriteLine($"[SYNC] ✓ Coverage plan {mrRawPlanId} synced successfully");
|
|
return true;
|
|
}
|
|
catch (HttpRequestException httpEx)
|
|
{
|
|
// Network-specific errors
|
|
Debug.WriteLine($"[SYNC] Network error for {mrRawPlanId}: {httpEx.Message}");
|
|
return false;
|
|
}
|
|
catch (TimeoutException timeoutEx)
|
|
{
|
|
// Timeout errors
|
|
Debug.WriteLine($"[SYNC] Timeout error for {mrRawPlanId}: {timeoutEx.Message}");
|
|
return false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// General errors
|
|
Debug.WriteLine($"[SYNC] Unexpected error for {mrRawPlanId}: {ex.Message}");
|
|
Debug.WriteLine($"[SYNC] Stack trace: {ex.StackTrace}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private bool ValidateCoveragePlan(Entities.Planning.MRRawPlan plan)
|
|
{
|
|
if (plan == null)
|
|
return false;
|
|
|
|
// Check required fields
|
|
if (plan.MRRawPlanId == Guid.Empty)
|
|
{
|
|
Debug.WriteLine("[SYNC] Invalid MRRawPlanId");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
#region Download Materials & Samplings
|
|
private async Task DownloadAllMaterials()
|
|
{
|
|
try
|
|
{
|
|
var allTransactions = await _productTransRepository.GetAllAsync();
|
|
|
|
foreach (var product in allTransactions)
|
|
{
|
|
var endpoint = _configuration["ApiSettings:Endpoints:GetProductFiles"];
|
|
var fileUrl = $"{endpoint}{Uri.EscapeDataString(product.ProductLink)}";
|
|
|
|
var material = new ProductMaterial
|
|
{
|
|
ProductTransId = product.ProductTransId,
|
|
ProductId = product.ProductId,
|
|
ProductName = product.ProductName,
|
|
ProductLink = product.ProductLink,
|
|
FileUrl = fileUrl,
|
|
FileExtension = Path.GetExtension(product.ProductLink)
|
|
};
|
|
|
|
if (!await _fileDownloadService.IsFileDownloadedAsync(product.ProductLink))
|
|
{
|
|
await DownloadMaterials(material);
|
|
}
|
|
}
|
|
|
|
// await Application.Current.MainPage.DisplayAlert("Success", "All materials downloaded!", "OK");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download all: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
private async Task DownloadMaterials(ProductMaterial material)
|
|
{
|
|
if (material == null || material.IsDownloading) return;
|
|
|
|
try
|
|
{
|
|
material.IsDownloading = true;
|
|
|
|
await _fileDownloadService.DownloadFileAsync(material.FileUrl, material.ProductLink, CancellationToken.None);
|
|
|
|
material.IsDownloaded = true;
|
|
material.IsDownloading = false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
material.IsDownloading = false;
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|