Files
taxbaik/TaxBaik.Infrastructure/Repositories/RevenueTrackingRepository.cs
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

73 lines
3.7 KiB
C#

namespace TaxBaik.Infrastructure.Repositories;
using Dapper;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
public class RevenueTrackingRepository(IDbConnectionFactory connectionFactory) : BaseRepository(connectionFactory), IRevenueTrackingRepository
{
public async Task<int> CreateAsync(RevenueTracking revenue, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.QueryFirstAsync<int>(
@"INSERT INTO revenue_tracking (client_id, invoice_number, invoice_date, service_type, amount, payment_status, payment_date, due_date, notes, created_at, updated_at)
VALUES (@ClientId, @InvoiceNumber, @InvoiceDate, @ServiceType, @Amount, @PaymentStatus, @PaymentDate, @DueDate, @Notes, NOW(), NOW())
RETURNING id",
revenue);
}
public async Task<IEnumerable<RevenueTracking>> GetByClientIdAsync(int clientId, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.QueryAsync<RevenueTracking>(
@"SELECT id, client_id, invoice_number, invoice_date, service_type, amount, payment_status, payment_date, due_date, notes, created_at, updated_at
FROM revenue_tracking WHERE client_id = @ClientId ORDER BY invoice_date DESC",
new { ClientId = clientId });
}
public async Task<IEnumerable<RevenueTracking>> GetPendingPaymentsAsync(CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.QueryAsync<RevenueTracking>(
@"SELECT id, client_id, invoice_number, invoice_date, service_type, amount, payment_status, payment_date, due_date, notes, created_at, updated_at
FROM revenue_tracking WHERE payment_status = 'pending' ORDER BY due_date ASC");
}
public async Task<IEnumerable<RevenueTracking>> GetByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
{
using var conn = Conn();
return await conn.QueryAsync<RevenueTracking>(
@"SELECT id, client_id, invoice_number, invoice_date, service_type, amount, payment_status, payment_date, due_date, notes, created_at, updated_at
FROM revenue_tracking WHERE invoice_date BETWEEN @StartDate AND @EndDate ORDER BY invoice_date DESC",
new { StartDate = startDate, EndDate = endDate });
}
public async Task UpdateAsync(RevenueTracking revenue, CancellationToken cancellationToken = default)
{
using var conn = Conn();
await conn.ExecuteAsync(
@"UPDATE revenue_tracking SET invoice_number = @InvoiceNumber, invoice_date = @InvoiceDate,
service_type = @ServiceType, amount = @Amount, payment_status = @PaymentStatus,
payment_date = @PaymentDate, due_date = @DueDate, notes = @Notes, updated_at = NOW()
WHERE id = @Id",
revenue);
}
public async Task MarkPaidAsync(int id, DateTime paymentDate, CancellationToken cancellationToken = default)
{
using var conn = Conn();
await conn.ExecuteAsync(
@"UPDATE revenue_tracking SET payment_status = 'paid', payment_date = @PaymentDate, updated_at = NOW() WHERE id = @Id",
new { Id = id, PaymentDate = paymentDate });
}
public async Task<decimal> GetTotalRevenueAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
{
using var conn = Conn();
var result = await conn.QueryFirstAsync<decimal>(
@"SELECT COALESCE(SUM(amount), 0) FROM revenue_tracking WHERE invoice_date BETWEEN @StartDate AND @EndDate",
new { StartDate = startDate, EndDate = endDate });
return result;
}
}