Files
taxbaik/TaxBaik.Application/Services/ConsultingActivityService.cs
T
kjh2064 c2955ad02f
TaxBaik CI/CD / build-and-deploy (push) Successful in 49s
feat: implement CRM and tax accounting specialized services and repositories
Phase 2: Repository Implementation (Dapper)
- TaxProfileRepository: tax profile CRUD + risk level analysis + filing due dates
- TaxFilingScheduleRepository: schedule tracking + upcoming due dates + completion marking
- ConsultingActivityRepository: CRM activity history + pending followups + consultant tracking
- ContractRepository: contract lifecycle + active contracts + expiring alerts + MRR calculation
- RevenueTrackingRepository: invoice tracking + payment status + revenue analysis

Phase 3: Service Layer (Business Logic)
- TaxProfileService: profile creation, risk assessment, upcoming filing detection
- TaxFilingScheduleService: schedule management, deadline tracking, completion workflow
- ConsultingActivityService: activity logging, followup management, consultant productivity
- ContractService: contract management, MRR calculation, expiring contract alerts
- RevenueTrackingService: invoice creation, payment tracking, revenue analytics

Phase 4: API Controller (REST Endpoints)
- TaxProfileController: CRUD operations + high-risk filtering + upcoming filings query

Architecture Highlights:
- SOLID principles: each layer has clear responsibility
- Dapper-based repositories for data access
- Comprehensive service layer for business logic
- RESTful API design with proper error handling
- Ready for Blazor UI implementation and deployment

Database Migration V015 executed:
- 5 new specialized tables for CRM and tax accounting
- Appropriate indexes for query performance
- Foreign key constraints for data integrity
2026-06-28 16:58:23 +09:00

48 lines
2.1 KiB
C#

namespace TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
public class ConsultingActivityService(IConsultingActivityRepository repository)
{
public async Task<int> CreateAsync(int clientId, string activityType, DateTime activityDate,
string description, int? consultantId = null, DateTime? nextFollowupDate = null, CancellationToken ct = default)
{
if (clientId <= 0)
throw new ValidationException("유효한 고객을 선택하세요.");
if (string.IsNullOrWhiteSpace(activityType))
throw new ValidationException("활동 유형을 입력하세요.");
if (string.IsNullOrWhiteSpace(description))
throw new ValidationException("활동 내용을 입력하세요.");
var activity = new ConsultingActivity
{
ClientId = clientId,
ActivityType = activityType.Trim(),
ActivityDate = activityDate,
Description = description.Trim(),
AssignedConsultantId = consultantId,
NextFollowupDate = nextFollowupDate,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
return await repository.CreateAsync(activity, ct);
}
public async Task<IEnumerable<ConsultingActivity>> GetByClientIdAsync(int clientId, CancellationToken ct = default) =>
await repository.GetByClientIdAsync(clientId, ct);
public async Task<IEnumerable<ConsultingActivity>> GetPendingFollowupsAsync(CancellationToken ct = default) =>
await repository.GetPendingFollowupsAsync(ct);
public async Task<IEnumerable<ConsultingActivity>> GetConsultantActivityAsync(int consultantId, DateTime fromDate, CancellationToken ct = default) =>
await repository.GetByConsultantAsync(consultantId, fromDate, ct);
public async Task UpdateAsync(int id, string? outcome, DateTime? nextFollowupDate, CancellationToken ct = default)
{
var activity = new ConsultingActivity { Id = id, Outcome = outcome, NextFollowupDate = nextFollowupDate, UpdatedAt = DateTime.UtcNow };
await repository.UpdateAsync(activity, ct);
}
}