import 'dart:developer'; import 'package:injectable/injectable.dart'; import 'package:isar/isar.dart'; import 'package:mnemo_cards_backend/discounts/discount_campaign_ext.dart'; import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import '../main.dart'; @lazySingleton class DiscountsManager { Future applyDiscount({ required Iterable discounts, required UserModel user, }) async { await isar.writeTxn(() async { await user.userData.load(); final userData = user.userData.value!; userData.activeDiscounts.addAll(discounts); await user.userData.save(); }); } Future changeCampaignStatus(DiscountCampaignModel campaign) async { final now = DateTime.now(); var target = campaign.status; if (campaign.status == DiscountCampaignModelStatus.created && now.isBetween(campaign.start, campaign.finish)) { target = DiscountCampaignModelStatus.active; } else if (campaign.status == DiscountCampaignModelStatus.active) { if (now.isBefore(campaign.start)) { target = DiscountCampaignModelStatus.created; } else if (now.isAfter(campaign.finish)) { target = DiscountCampaignModelStatus.expired; } } if (target != campaign.status) { await isar.writeTxn(() => isar.discountCampaignModels.put( campaign.copyWith(status: target), )); } } Future applyCampaign(DiscountCampaignModel campaign) async { final now = DateTime.now(); if (!now.isBetween(campaign.start, campaign.finish)) { if (now.isBefore(campaign.start)) { return DiscountError(DiscountErrorType.notStarted); } return DiscountError(DiscountErrorType.expired); } // final usersData = await isar.txn( // () => isar.userDataModels // .filter() // .optional( // campaign.tags.isNotEmpty, // (q) => q.anyOf( // campaign.tags, // (q, tag) => q.tagsElementContains(tag), // ), // ) // .findAll(), // ); return null; } Future removeDiscount({ required DiscountCampaignModel campaign, required UserModel user, }) async { await isar.writeTxn(() async { await user.userData.load(); final userData = user.userData.value!; userData.activeDiscounts.removeAll(campaign.discounts); await isar.userDataModels.put(userData); }); } Future getProductDiscount( MnemoCardsProductModel model, UserDataModel userData, ) async { double maxDiscount = 0; for (final discount in userData.activeDiscounts) { if (discount.discountPercent > maxDiscount && discount.products.any((p) => p.equalsProduct(model))) { maxDiscount = discount.discountPercent; } } final activeCampaigns = await discountCampaignsForUser(userData, product: model); for (final campaign in activeCampaigns) { for (final discount in campaign.discounts) { if (discount.discountPercent > maxDiscount && discount.products.any((p) => p.equalsProduct(model))) { maxDiscount = discount.discountPercent; } } } return maxDiscount; } Future> discountCampaignsForUser( UserDataModel userModel, { MnemoCardsProductModel? product, }) async { final activeCampaigns = await isar.txn(() async => isar.discountCampaignModels .filter() .statusEqualTo(DiscountCampaignModelStatus.active) .optional(userModel.tags.isEmpty, (q) => q.tagsIsEmpty()) .optional( product != null, (q) => q.discounts( (d) => d.productsElement( (p) => p.typeEqualTo(product!.type).productIdEqualTo( product.id, ), ), ), ) .findAll()); if (userModel.tags.isEmpty) { return activeCampaigns; } else { // todo check tags in query final userTags = userModel.tags.toSet(); return activeCampaigns .where((campaign) => campaign.tags.hasIntersection(userTags)) .toList(); } } Future> discountCampaigns() async { final models = await isar .txn(() async => isar.discountCampaignModels.where().findAll()); final dtos = await Future.wait(models.map((m) async => await m.toDto())); return dtos; } Future deleteDiscountCampaign(String stringId) async { try { final id = int.parse(stringId); isar.writeTxn(() async { final model = await isar.discountCampaignModels.get(id); if (model == null) { return 'Campaign $id not found'; } if (model.status != DiscountCampaignModelStatus.disabled) { return 'Disable campaign before deleting'; } isar.discountCampaignModels.delete(id); }); return null; } catch (e, s) { log('Error when deleting discount campaign', error: e, stackTrace: s); return 'Error $e'; } } Future addDiscount(DiscountCampaignDto dto) async { try { final model = DiscountCampaignModel( id: dto.id, start: dto.start, finish: dto.finish, status: dto.status.toModel(), name: dto.name, tags: dto.tags, ); final discounts = dto.discounts.map((d) => d.toModel()).toList(); await isar.writeTxn(() async { await isar.discountModels.putAll(discounts); await isar.discountCampaignModels.put(model); await model.discounts.reset(); model.discounts.addAll(discounts); await model.discounts.save(); }); return true; } catch (e, s) { print('error when adding campaign'); log('error when adding campaign', error: e, stackTrace: s); return false; } } const DiscountsManager(); } class DiscountError { final String? message; final DiscountErrorType type; DiscountError(this.type, {this.message}); } enum DiscountErrorType { notAvailable, notStarted, expired, }