diff --git a/Marco.Pms.Model/Dtos/PurchaseInvoice/PurchaseInvoiceDto.cs b/Marco.Pms.Model/Dtos/PurchaseInvoice/PurchaseInvoiceDto.cs index 0688aac..0830cd5 100644 --- a/Marco.Pms.Model/Dtos/PurchaseInvoice/PurchaseInvoiceDto.cs +++ b/Marco.Pms.Model/Dtos/PurchaseInvoice/PurchaseInvoiceDto.cs @@ -10,6 +10,7 @@ public required string ShippingAddress { get; set; } public string? PurchaseOrderNumber { get; set; } public DateTime? PurchaseOrderDate { get; set; } + public Guid? StatusId { get; set; } public required Guid SupplierId { get; set; } public string? ProformaInvoiceNumber { get; set; } public DateTime? ProformaInvoiceDate { get; set; } @@ -26,6 +27,6 @@ public double? TransportCharges { get; set; } public required double TotalAmount { get; set; } public DateTime? PaymentDueDate { get; set; } // Defaults to 40 days from the invoice date - public List? Attachments { get; set; } + public List Attachments { get; set; } = new List(); } } diff --git a/Marco.Pms.Services/Controllers/PurchaseInvoiceController.cs b/Marco.Pms.Services/Controllers/PurchaseInvoiceController.cs index 26c1f5e..0b80396 100644 --- a/Marco.Pms.Services/Controllers/PurchaseInvoiceController.cs +++ b/Marco.Pms.Services/Controllers/PurchaseInvoiceController.cs @@ -1,6 +1,9 @@ -using Marco.Pms.Model.Dtos.PurchaseInvoice; +using AutoMapper; +using Marco.Pms.Model.Dtos.PurchaseInvoice; +using Marco.Pms.Model.Utilities; using Marco.Pms.Services.Service.ServiceInterfaces; using MarcoBMS.Services.Helpers; +using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; namespace Marco.Pms.Services.Controllers @@ -12,16 +15,20 @@ namespace Marco.Pms.Services.Controllers private readonly UserHelper _userHelper; private readonly IPurchaseInvoiceService _purchaseInvoiceService; private readonly ISignalRService _signalR; + private readonly IServiceScopeFactory _serviceScopeFactory; private readonly Guid tenantId; - public PurchaseInvoiceController(UserHelper userHelper, IPurchaseInvoiceService purchaseInvoiceService, ISignalRService signalR) + public PurchaseInvoiceController(UserHelper userHelper, IPurchaseInvoiceService purchaseInvoiceService, ISignalRService signalR, IServiceScopeFactory serviceScopeFactory) { _userHelper = userHelper; _purchaseInvoiceService = purchaseInvoiceService; tenantId = _userHelper.GetTenantId(); _signalR = signalR; + _serviceScopeFactory = serviceScopeFactory; } + #region =================================================================== Purchase Invoice Functions =================================================================== + /// /// Retrieves a list of purchase invoices based on search string, filter, activity status, page size, and page number. /// @@ -90,5 +97,36 @@ namespace Marco.Pms.Services.Controllers // Return the HTTP response return StatusCode(response.StatusCode, response); } + + [HttpPatch("edit/{id}")] + public async Task EditPurchaseInvoice(Guid id, [FromBody] JsonPatchDocument patchDoc, CancellationToken ct) + { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var existingPurchaseInvoice = await _purchaseInvoiceService.GetPurchaseInvoiceByIdAsync(id, tenantId, ct); + if (existingPurchaseInvoice == null) + return NotFound(ApiResponse.ErrorResponse("Invalid purchase invoice ID", "Invalid purchase invoice ID", 404)); + + using var scope = _serviceScopeFactory.CreateScope(); + var mapper = scope.ServiceProvider.GetRequiredService(); + var modelToPatch = mapper.Map(existingPurchaseInvoice); + + // Apply the JSON Patch document to the DTO and check model state validity + patchDoc.ApplyTo(modelToPatch, ModelState); + if (!ModelState.IsValid) + { + return BadRequest(ApiResponse.ErrorResponse("Validation failed", "Provided patch document values are invalid", 400)); + } + + var response = await _purchaseInvoiceService.UpdatePurchaseInvoiceAsync(id, existingPurchaseInvoice, modelToPatch, loggedInEmployee, tenantId, ct); + return StatusCode(response.StatusCode, response); + } + + #endregion + + #region =================================================================== Delivery Challan Functions =================================================================== + #endregion + + #region =================================================================== Purchase Invoice History Functions =================================================================== + #endregion } } diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index 2b0a06f..631e943 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -628,6 +628,9 @@ namespace Marco.Pms.Services.MappingProfiles .ForMember( dest => dest.PaymentDueDate, opt => opt.MapFrom(src => src.PaymentDueDate.HasValue ? src.PaymentDueDate : DateTime.UtcNow.AddDays(40))); + + CreateMap(); + CreateMap() .ForMember( dest => dest.PurchaseInvoiceUId, diff --git a/Marco.Pms.Services/Service/PurchaseInvoiceService.cs b/Marco.Pms.Services/Service/PurchaseInvoiceService.cs index 697af23..aabda5e 100644 --- a/Marco.Pms.Services/Service/PurchaseInvoiceService.cs +++ b/Marco.Pms.Services/Service/PurchaseInvoiceService.cs @@ -1,8 +1,10 @@ using AutoMapper; using Marco.Pms.DataAccess.Data; +using Marco.Pms.Helpers.Utility; using Marco.Pms.Model.Dtos.PurchaseInvoice; using Marco.Pms.Model.Employees; using Marco.Pms.Model.Filters; +using Marco.Pms.Model.MongoDBModels.Utility; using Marco.Pms.Model.OrganizationModel; using Marco.Pms.Model.Projects; using Marco.Pms.Model.PurchaseInvoice; @@ -41,136 +43,7 @@ namespace Marco.Pms.Services.Service _s3Service = s3Service; _mapper = mapper; } - - //public async Task> GetPurchaseInvoiceListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, - // Employee loggedInEmployee, Guid tenantId, CancellationToken ct) - //{ - // _logger.LogInfo("GetPurchaseInvoiceListAsync called for TenantId: {TenantId}, EmployeeId: {EmployeeId}", tenantId, loggedInEmployee.Id); - - // await using var _context = await _dbContextFactory.CreateDbContextAsync(ct); - - // //var purchaseInvoices = _context.PurchaseInvoiceDetails - // var query = _context.PurchaseInvoiceDetails - // .Include(pid => pid.Organization) - // .Include(pid => pid.Supplier) - // .Include(pid => pid.Status) - // .Where(pid => pid.IsActive == isActive && pid.TenantId == tenantId); - - // var advanceFilter = TryDeserializeFilter(filter); - - // query = query.ApplyCustomFilters(advanceFilter, "CreatedAt"); - - // if (advanceFilter != null) - // { - // if (advanceFilter.Filters != null) - // { - // query = query.ApplyListFilters(advanceFilter.Filters); - // } - // if (advanceFilter.DateFilter != null) - // { - // query = query.ApplyDateFilter(advanceFilter.DateFilter); - // } - // if (advanceFilter.SearchFilters != null) - // { - // var invoiceSearchFilter = advanceFilter.SearchFilters.Where(f => f.Column != "ProjectName" || f.Column != "Project").ToList(); - // if (invoiceSearchFilter.Any()) - // { - // query = query.ApplySearchFilters(invoiceSearchFilter); - // } - // } - // if (!string.IsNullOrWhiteSpace(advanceFilter.GroupByColumn)) - // { - // query = query.ApplyGroupByFilters(advanceFilter.GroupByColumn); - // } - // } - - // bool isProjectFilter = false; - - // var infraProjectTask = Task.Run(async () => - // { - // await using var context = await _dbContextFactory.CreateDbContextAsync(); - - // var infraProjectsQuery = context.Projects.Where(p => p.TenantId == tenantId); - - // if (advanceFilter?.SearchFilters != null && advanceFilter.SearchFilters.Any()) - // { - // var projectSearchFilter = advanceFilter.SearchFilters - // .Where(f => f.Column == "ProjectName" || f.Column == "Project") - // .Select(f => new SearchItem { Column = "Name", Value = f.Value }) - // .ToList(); - // if (projectSearchFilter.Any()) - // { - // infraProjectsQuery = infraProjectsQuery.ApplySearchFilters(projectSearchFilter); - // isProjectFilter = true; - // } - // } - // return await infraProjectsQuery.Select(p => _mapper.Map(p)).ToListAsync(); - // }); - - // var serviceProjectTask = Task.Run(async () => - // { - // await using var context = await _dbContextFactory.CreateDbContextAsync(); - - // var serviceProjectsQuery = context.ServiceProjects.Where(sp => sp.TenantId == tenantId); - - // if (advanceFilter?.SearchFilters != null && advanceFilter.SearchFilters.Any()) - // { - // var projectSearchFilter = advanceFilter.SearchFilters - // .Where(f => f.Column == "ProjectName" || f.Column == "Project") - // .Select(f => new SearchItem { Column = "Name", Value = f.Value }) - // .ToList(); - // if (projectSearchFilter.Any()) - // { - // serviceProjectsQuery = serviceProjectsQuery.ApplySearchFilters(projectSearchFilter); - // isProjectFilter = true; - // } - // } - - // return await serviceProjectsQuery.Select(sp => _mapper.Map(sp)).ToListAsync(); - // }); - - // await Task.WhenAll(infraProjectTask, serviceProjectTask); - - // var projects = infraProjectTask.Result; - // projects.AddRange(serviceProjectTask.Result); - - // if (isProjectFilter) - // { - // var projectIds = projects.Select(p => p.Id).ToList(); - // query = query.Where(pid => projectIds.Contains(pid.ProjectId)); - // } - - // var totalCount = await query.CountAsync(ct); - - // var purchaseInvoices = await query - // .Skip((pageNumber - 1) * pageSize) - // .Take(pageSize) - // .ToListAsync(); - - // var totalPages = (int)Math.Ceiling((double)totalCount / pageSize); - - // var response = purchaseInvoices.Select(pi => - // { - // var result = _mapper.Map(pi); - // result.Project = projects.FirstOrDefault(p => p.Id == pi.ProjectId); - // return result; - // }).ToList(); - - // var pagedResult = new - // { - // CurrentPage = pageNumber, - // PageSize = pageSize, - // TotalPages = totalPages, - // TotalCount = totalCount, - // HasPrevious = pageNumber > 1, - // HasNext = pageNumber < totalPages, - // Data = response - // }; - - // return ApiResponse.SuccessResponse(pagedResult, "Invoice list fetched successfully", 200); - //} - - + #region =================================================================== Purchase Invoice Functions =================================================================== /// /// Retrieves a paged list of purchase invoices for a given tenant with support for /// advanced filters, project-based search across Infra and Service projects, and @@ -611,7 +484,7 @@ namespace Marco.Pms.Services.Service // Generate Invoice ID early for S3 folder structure var newInvoiceId = Guid.NewGuid(); - if (model.Attachments?.Any() == true) + if (model.Attachments.Any()) { var batchId = Guid.NewGuid(); @@ -761,7 +634,281 @@ namespace Marco.Pms.Services.Service } } + public async Task> UpdatePurchaseInvoiceAsync(Guid id, PurchaseInvoiceDetails purchaseInvoice, PurchaseInvoiceDto model, Employee loggedInEmployee, Guid tenantId, CancellationToken ct) + { + // Validate input arguments and log warnings for invalid cases. + if (id == Guid.Empty) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync called with empty invoice Id. TenantId: {TenantId}, EmployeeId: {EmployeeId}", tenantId, loggedInEmployee.Id); + return ApiResponse.ErrorResponse( + "Invalid invoice identifier", + "The purchase invoice identifier cannot be empty.", + 400); + } + + if (tenantId == Guid.Empty) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync called with empty tenant Id. InvoiceId: {InvoiceId}, EmployeeId: {EmployeeId}", id, loggedInEmployee.Id); + + return ApiResponse.ErrorResponse( + "Invalid tenant identifier", + "The tenant identifier cannot be empty.", + 400); + } + + _logger.LogInfo("Starting UpdatePurchaseInvoiceAsync. InvoiceId: {InvoiceId}, TenantId: {TenantId}, EmployeeId: {EmployeeId}", id, tenantId, loggedInEmployee.Id); + + await using var context = await _dbContextFactory.CreateDbContextAsync(ct); + await using var transaction = await context.Database.BeginTransactionAsync(); + try + { + // Scoped helper service for update logs. + using var scope = _serviceScopeFactory.CreateScope(); + var updateLogHelper = scope.ServiceProvider.GetRequiredService(); + + // 1. Validate existence of Project (Infra or Service). + var infraProject = await context.Projects + .AsNoTracking() + .Where(p => p.Id == model.ProjectId && p.TenantId == tenantId) + .Select(p => _mapper.Map(p)) + .FirstOrDefaultAsync(ct); + + BasicProjectVM? projectVm; + if (infraProject == null) + { + var serviceProject = await context.ServiceProjects + .AsNoTracking() + .Where(sp => sp.Id == model.ProjectId && sp.IsActive && sp.TenantId == tenantId) + .Select(p => _mapper.Map(p)) + .FirstOrDefaultAsync(ct); + + if (serviceProject == null) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync failed: Project {ProjectId} not found for Tenant {TenantId}", model.ProjectId, tenantId); + + return ApiResponse.ErrorResponse("Project not found", "The specified project does not exist.", 404); + } + projectVm = serviceProject; + } + else + { + projectVm = infraProject; + } + + // 2. Validate Organization. + var organization = await context.Organizations + .AsNoTracking() + .FirstOrDefaultAsync(o => o.Id == model.OrganizationId && o.IsActive, ct); + + if (organization == null) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync failed: Organization {OrganizationId} not found or inactive.", model.OrganizationId); + + return ApiResponse.ErrorResponse("Organization not found", "The selected organization is invalid or inactive.", 404); + } + + // 3. Validate Supplier. + var supplier = await context.Organizations + .AsNoTracking() + .FirstOrDefaultAsync(o => o.Id == model.SupplierId && o.IsActive, ct); + + if (supplier == null) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync failed: Supplier {SupplierId} not found or inactive.", model.SupplierId); + + return ApiResponse.ErrorResponse("Supplier not found", "The selected supplier is invalid or inactive.", 404); + } + + // 4. Validate PurchaseInvoiceStatus. + var status = await context.PurchaseInvoiceStatus + .AsNoTracking() + .FirstOrDefaultAsync(s => s.Id == model.StatusId, ct); + + if (status == null) + { + _logger.LogError(null, "UpdatePurchaseInvoiceAsync critical: Missing required purchase invoice status ID {StatusId}.", model.StatusId); + + return ApiResponse.ErrorResponse( + "System configuration error", + "Required purchase invoice status configuration is missing in the system.", + 500); + } + + // Save previous state for audit/logging. + var existingEntityBson = updateLogHelper.EntityToBsonDocument(purchaseInvoice); + + // Map updated fields from DTO to entity. + _mapper.Map(model, purchaseInvoice); + purchaseInvoice.UpdatedAt = DateTime.UtcNow; + purchaseInvoice.UpdatedById = loggedInEmployee.Id; + + context.PurchaseInvoiceDetails.Update(purchaseInvoice); + await context.SaveChangesAsync(ct); + + // 5. Handle attachments update. + var newAttachments = model.Attachments.Where(a => a.IsActive).ToList(); + var deleteAttachmentIds = model.Attachments + .Where(a => a.DocumentId.HasValue && !a.IsActive) + .Select(a => a.DocumentId!.Value) + .ToList(); + + if (newAttachments.Any()) + { + var batchId = Guid.NewGuid(); + + // Validate attachment types. + var typeIds = newAttachments.Select(a => a.InvoiceAttachmentTypeId).Distinct().ToList(); + var validTypes = await context.InvoiceAttachmentTypes + .Where(iat => typeIds.Contains(iat.Id)) + .ToListAsync(ct); + + var invalidTypeIds = typeIds.Except(validTypes.Select(t => t.Id)).ToList(); + if (invalidTypeIds.Any()) + { + foreach (var invalidId in invalidTypeIds) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync failed: Invalid attachment type ID {AttachmentTypeId}.", invalidId); + } + return ApiResponse.ErrorResponse("Invalid attachment types", $"One or more attachment types are invalid: {string.Join(", ", invalidTypeIds)}", 400); + } + + var preparedDocuments = new List(); + var preparedAttachments = new List(); + + // Process each new attachment. + foreach (var attachment in newAttachments) + { + // Validate base64 data presence. + var base64Data = attachment.Base64Data?.Split(',').LastOrDefault(); + if (string.IsNullOrWhiteSpace(base64Data)) + { + _logger.LogWarning("UpdatePurchaseInvoiceAsync failed: Attachment '{FileName}' contains no data.", attachment.FileName ?? ""); + + return ApiResponse.ErrorResponse("Invalid attachment", $"Attachment '{attachment.FileName ?? ""}' contains no valid data.", 400); + } + + // Determine content type with fallback. + var fileType = _s3Service.GetContentTypeFromBase64(base64Data); + var safeFileType = string.IsNullOrEmpty(fileType) ? "application/octet-stream" : fileType; + + var fileName = attachment.FileName ?? _s3Service.GenerateFileName(safeFileType, tenantId, "invoice"); + var objectKey = $"tenant-{tenantId}/PurchaseInvoice/{id}/{fileName}"; + + // Upload file to S3 asynchronously. + await _s3Service.UploadFileAsync(base64Data, safeFileType, objectKey); + + var documentId = Guid.NewGuid(); + + // Prepare Document entity. + preparedDocuments.Add(new Document + { + Id = documentId, + BatchId = batchId, + UploadedById = loggedInEmployee.Id, + FileName = fileName, + ContentType = attachment.ContentType ?? safeFileType, + S3Key = objectKey, + FileSize = attachment.FileSize, + UploadedAt = DateTime.UtcNow, + TenantId = tenantId, + }); + + // Prepare PurchaseInvoiceAttachment entity. + preparedAttachments.Add(new PurchaseInvoiceAttachment + { + Id = Guid.NewGuid(), + InvoiceAttachmentTypeId = attachment.InvoiceAttachmentTypeId, + PurchaseInvoiceId = id, + DocumentId = documentId, + UploadedAt = DateTime.UtcNow, + UploadedById = loggedInEmployee.Id, + TenantId = tenantId, + }); + } + + // Add batched uploaded documents and attachments. + context.Documents.AddRange(preparedDocuments); + context.PurchaseInvoiceAttachments.AddRange(preparedAttachments); + await context.SaveChangesAsync(ct); + } + + // Delete attachments marked for removal. + if (deleteAttachmentIds.Any()) + { + await DeleteAttachemnts(deleteAttachmentIds, ct); + _logger.LogInfo("Deleted {Count} attachments from PurchaseInvoiceId {InvoiceId} for TenantId {TenantId}", deleteAttachmentIds.Count, id, tenantId); + } + + await transaction.CommitAsync(); + + // Push audit log entry asynchronously for traceability. + await updateLogHelper.PushToUpdateLogsAsync( + new UpdateLogsObject + { + EntityId = id.ToString(), + UpdatedById = loggedInEmployee.Id.ToString(), + OldObject = existingEntityBson, + UpdatedAt = DateTime.UtcNow + }, + "PurchaseInvoiceModificationLog"); + + _logger.LogInfo("Purchase invoice updated successfully. InvoiceId: {InvoiceId}, TenantId: {TenantId}, UpdatedById: {UserId}", id, tenantId, loggedInEmployee.Id); + + return ApiResponse.SuccessResponse(model, "Purchase invoice updated successfully.", 200); + } + catch (OperationCanceledException) + { + await transaction.RollbackAsync(); + _logger.LogError(null, "UpdatePurchaseInvoiceAsync operation cancelled. InvoiceId: {InvoiceId}, TenantId: {TenantId}", id, tenantId); + + return ApiResponse.ErrorResponse("Request cancelled", "The update operation was cancelled by the client or the server.", 499); + } + catch (DbUpdateException ex) + { + await transaction.RollbackAsync(); + _logger.LogError(ex, "Unexpected error during update of purchase invoice. InvoiceId: {InvoiceId}, TenantId: {TenantId}, UserId: {UserId}", id, tenantId, loggedInEmployee.Id); + + return ApiResponse.ErrorResponse("Update failed", "An unexpected error occurred while updating the purchase invoice. Please try again later.", 500); + } + catch (Exception ex) + { + _logger.LogError(ex, "Unexpected error during update of purchase invoice. InvoiceId: {InvoiceId}, TenantId: {TenantId}, UserId: {UserId}", id, tenantId, loggedInEmployee.Id); + + return ApiResponse.ErrorResponse("Update failed", "An unexpected error occurred while updating the purchase invoice. Please try again later.", 500); + } + } + + //public async Task DeletePurchaseInvoiceAsync(Guid id, Guid tenantId, CancellationToken ct = default) + + #endregion + + #region =================================================================== Delivery Challan Functions =================================================================== + #endregion + + #region =================================================================== Purchase Invoice History Functions =================================================================== + #endregion + + #region =================================================================== Helper Functions =================================================================== + + public async Task GetPurchaseInvoiceByIdAsync(Guid id, Guid tenantId, CancellationToken ct = default) + { + await using var readContext = await _dbContextFactory.CreateDbContextAsync(ct); + var purchaseInvoice = await readContext.PurchaseInvoiceDetails + .Where(e => e.Id == id && e.TenantId == tenantId && e.IsActive) + .FirstOrDefaultAsync(ct); + + if (purchaseInvoice == null) + { + _logger.LogWarning("Purchase Invoice not found. ID: {Id}, TenantID: {TenantId}", id, tenantId); + } + else + { + _logger.LogInfo("Purchase Invoice found. ID: {Id}, TenantID: {TenantId}", id, tenantId); + } + + return purchaseInvoice; + } private AdvanceFilter? TryDeserializeFilter(string? filter) { if (string.IsNullOrWhiteSpace(filter)) @@ -816,8 +963,54 @@ namespace Marco.Pms.Services.Service private async Task LoadServiceProjectAsync(Guid projectId, Guid tenantId) { await using var context = await _dbContextFactory.CreateDbContextAsync(); - return await context.ServiceProjects.Where(sp => sp.Id == projectId && sp.TenantId == tenantId).Select(sp => _mapper.Map(sp)).FirstOrDefaultAsync(); + return await context.ServiceProjects.AsNoTracking().Where(sp => sp.Id == projectId && sp.TenantId == tenantId).Select(sp => _mapper.Map(sp)).FirstOrDefaultAsync(); } + private async Task DeleteAttachemnts(List documentIds, CancellationToken ct) + { + var attachmentTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + var attachments = await context.PurchaseInvoiceAttachments.AsNoTracking().Where(ba => documentIds.Contains(ba.DocumentId)).ToListAsync(ct); + + context.PurchaseInvoiceAttachments.RemoveRange(attachments); + await context.SaveChangesAsync(ct); + }); + var documentsTask = Task.Run(async () => + { + using var scope = _serviceScopeFactory.CreateScope(); + var _updateLogHelper = scope.ServiceProvider.GetRequiredService(); + + await using var context = await _dbContextFactory.CreateDbContextAsync(); + var documents = await context.Documents.AsNoTracking().Where(ba => documentIds.Contains(ba.Id)).ToListAsync(ct); + + if (documents.Any()) + { + context.Documents.RemoveRange(documents); + await context.SaveChangesAsync(ct); + + List deletionObject = new List(); + foreach (var document in documents) + { + deletionObject.Add(new S3DeletionObject + { + Key = document.S3Key + }); + if (!string.IsNullOrWhiteSpace(document.ThumbS3Key) && document.ThumbS3Key != document.S3Key) + { + deletionObject.Add(new S3DeletionObject + { + Key = document.ThumbS3Key + }); + } + } + await _updateLogHelper.PushToS3DeletionAsync(deletionObject); + } + }); + + await Task.WhenAll(attachmentTask, documentsTask); + } + #endregion + } } diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IPurchaseInvoiceService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IPurchaseInvoiceService.cs index 554da76..3f194f9 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IPurchaseInvoiceService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IPurchaseInvoiceService.cs @@ -1,5 +1,6 @@ using Marco.Pms.Model.Dtos.PurchaseInvoice; using Marco.Pms.Model.Employees; +using Marco.Pms.Model.PurchaseInvoice; using Marco.Pms.Model.Utilities; using Marco.Pms.Model.ViewModels.PurchaseInvoice; @@ -7,9 +8,23 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces { public interface IPurchaseInvoiceService { + #region =================================================================== Purchase Invoice Functions =================================================================== Task> GetPurchaseInvoiceListAsync(string? searchString, string? filter, bool isActive, int pageSize, int pageNumber, Employee loggedInEmployee, Guid tenantId, CancellationToken ct); Task> GetPurchaseInvoiceDetailsAsync(Guid id, Employee loggedInEmployee, Guid tenantId, CancellationToken ct); Task> CreatePurchaseInvoiceAsync(PurchaseInvoiceDto model, Employee loggedInEmployee, Guid tenantId, CancellationToken ct); + Task> UpdatePurchaseInvoiceAsync(Guid id, PurchaseInvoiceDetails purchaseInvoice, PurchaseInvoiceDto model, Employee loggedInEmployee, Guid tenantId, CancellationToken ct); + + #endregion + + #region =================================================================== Delivery Challan Functions =================================================================== + #endregion + + #region =================================================================== Purchase Invoice History Functions =================================================================== + #endregion + + #region =================================================================== Helper Functions =================================================================== + Task GetPurchaseInvoiceByIdAsync(Guid id, Guid tenantId, CancellationToken ct); + #endregion } }