import 'package:flutter/foundation.dart'; import 'package:intl/intl.dart'; import '../models/receivable_models.dart'; import '../models/shipment_models.dart'; import 'app_settings_repository.dart'; import 'calendar_sync_service.dart'; /// Maps domain entities (shipments, receivables, etc.) to Google Calendar events. class BusinessCalendarMapper { BusinessCalendarMapper({ CalendarSyncService? calendarSyncService, AppSettingsRepository? settingsRepository, }) : _calendarSyncService = calendarSyncService ?? CalendarSyncService(), _settingsRepository = settingsRepository ?? AppSettingsRepository(); final CalendarSyncService _calendarSyncService; final AppSettingsRepository _settingsRepository; final NumberFormat _currencyFormat = NumberFormat.currency(locale: 'ja_JP', symbol: '¥'); Future _ensureReady() async { final enabled = await _settingsRepository.getGoogleCalendarEnabled(); if (!enabled) return false; final ready = await _calendarSyncService.ensureSignedIn(); return ready; } Future syncShipments(List shipments) async { if (shipments.isEmpty) return; if (!await _ensureReady()) return; for (final shipment in shipments) { await _syncShipment(shipment); } } Future syncShipment(Shipment shipment) async { if (!await _ensureReady()) return; await _syncShipment(shipment); } Future syncReceivables(List summaries) async { if (summaries.isEmpty) return; if (!await _ensureReady()) return; for (final summary in summaries) { await _syncReceivable(summary); } } Future syncReceivable(ReceivableInvoiceSummary summary) async { if (!await _ensureReady()) return; await _syncReceivable(summary); } Future _syncShipment(Shipment shipment) async { final date = shipment.scheduledShipDate ?? shipment.actualShipDate; if (date == null) return; final start = DateTime(date.year, date.month, date.day, 9); final end = start.add(const Duration(hours: 2)); final summary = '[出荷] ${(shipment.customerNameSnapshot ?? '取引先未設定')}'; final buffer = StringBuffer() ..writeln('受注番号: ${shipment.orderNumberSnapshot ?? '-'}') ..writeln('ステータス: ${shipment.status.displayName}') ..writeln('配送業者: ${shipment.carrierName ?? '-'}') ..writeln('追跡番号: ${shipment.trackingNumber ?? '-'}'); if (shipment.trackingUrl?.isNotEmpty == true) { buffer.writeln('トラッキングURL: ${shipment.trackingUrl}'); } try { await _calendarSyncService.createOrUpdateEvent( eventId: 'shipment-${shipment.id}', summary: summary, description: buffer.toString(), start: start, end: end, extendedProperties: { 'type': 'shipment', 'shipmentId': shipment.id, if (shipment.orderNumberSnapshot != null) 'orderNumber': shipment.orderNumberSnapshot!, if (shipment.customerNameSnapshot != null) 'customer': shipment.customerNameSnapshot!, }, ); } catch (e, stack) { debugPrint('Failed to sync shipment ${shipment.id} to calendar: $e\n$stack'); } } Future _syncReceivable(ReceivableInvoiceSummary summary) async { final dueDate = summary.dueDate; final start = DateTime(dueDate.year, dueDate.month, dueDate.day, 10); final end = start.add(const Duration(hours: 1)); final title = '[入金予定] ${summary.customerName}'; final description = StringBuffer() ..writeln('請求書番号: ${summary.invoiceNumber}') ..writeln('請求日: ${DateFormat('yyyy/MM/dd').format(summary.invoiceDate)}') ..writeln('期日: ${DateFormat('yyyy/MM/dd').format(summary.dueDate)}') ..writeln('請求額: ${_currencyFormat.format(summary.totalAmount)}') ..writeln('入金済み: ${_currencyFormat.format(summary.paidAmount)}') ..writeln('残高: ${_currencyFormat.format(summary.outstandingAmount)}'); try { await _calendarSyncService.createOrUpdateEvent( eventId: 'receivable-${summary.invoiceId}', summary: title, description: description.toString(), start: start, end: end, extendedProperties: { 'type': 'receivable', 'invoiceId': summary.invoiceId, 'invoiceNumber': summary.invoiceNumber, }, ); } catch (e, stack) { debugPrint('Failed to sync receivable ${summary.invoiceId} to calendar: $e\n$stack'); } } }