@@ -30,8 +30,9 @@ public interface PromotionalPaymentServiceDriver extends PaymentServiceDriver { | |||||
AccountPlan accountPlan, | AccountPlan accountPlan, | ||||
AccountPaymentMethod paymentMethod) { | AccountPaymentMethod paymentMethod) { | ||||
// do not use if deleted (should never happen) | // do not use if deleted (should never happen) | ||||
// do not use if wrong currency (should never happen) | |||||
// do not use if other higher priority promotions are usable | // do not use if other higher priority promotions are usable | ||||
return paymentMethod.notDeleted() && usable.isEmpty(); | |||||
return paymentMethod.notDeleted() && promo.isCurrency(bill.getCurrency()) && usable.isEmpty(); | |||||
} | } | ||||
} | } |
@@ -8,6 +8,9 @@ import org.hibernate.criterion.Order; | |||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.stereotype.Repository; | import org.springframework.stereotype.Repository; | ||||
import java.util.Set; | |||||
import java.util.stream.Collectors; | |||||
@Repository | @Repository | ||||
public class BubblePlanDAO extends AccountOwnedEntityDAO<BubblePlan> { | public class BubblePlanDAO extends AccountOwnedEntityDAO<BubblePlan> { | ||||
@@ -38,4 +41,8 @@ public class BubblePlanDAO extends AccountOwnedEntityDAO<BubblePlan> { | |||||
return findByName(id); | return findByName(id); | ||||
} | } | ||||
public Set<String> getSupportedCurrencies () { | |||||
return findAll().stream().map(BubblePlan::getCurrency).collect(Collectors.toSet()); | |||||
} | |||||
} | } |
@@ -25,27 +25,47 @@ public class PromotionDAO extends AbstractCRUDDAO<Promotion> { | |||||
return found != null ? found : findByName(id); | return found != null ? found : findByName(id); | ||||
} | } | ||||
public Promotion findEnabledAndActiveWithCode(String code) { | |||||
return filterActive(findByUniqueFields("enabled", true, "code", code, "referral", false, "adminAssignOnly", false)); | |||||
public Promotion findEnabledAndActiveWithCode(String code, String currency) { | |||||
return filterActive(findByUniqueFields( | |||||
"enabled", true, | |||||
"code", code, | |||||
"referral", false, | |||||
"currency", currency, | |||||
"adminAssignOnly", false)); | |||||
} | } | ||||
public List<Promotion> findEnabledAndActiveWithNoCode() { | |||||
return filterActive(findByFields("enabled", true, "code", null, "referral", false, "adminAssignOnly", false)); | |||||
public List<Promotion> findEnabledAndActiveWithNoCode(String currency) { | |||||
return filterActive(findByFields( | |||||
"enabled", true, | |||||
"code", null, | |||||
"referral", false, | |||||
"currency", currency, | |||||
"adminAssignOnly", false)); | |||||
} | } | ||||
public List<Promotion> findVisibleAndEnabledAndActiveWithNoCodeOrWithCode(String code) { | |||||
public List<Promotion> findVisibleAndEnabledAndActiveWithNoCodeOrWithCode(String code, String currency) { | |||||
if (empty(code)) { | if (empty(code)) { | ||||
return filterActive(findByFields("enabled", true, "code", null, "visible", true, "adminAssignOnly", false)); | |||||
return filterActive(findByFields( | |||||
"enabled", true, | |||||
"code", null, | |||||
"visible", true, | |||||
"currency", currency, | |||||
"adminAssignOnly", false)); | |||||
} else { | } else { | ||||
return filterActive(list(criteria().add(and( | return filterActive(list(criteria().add(and( | ||||
eq("enabled", true), | eq("enabled", true), | ||||
eq("visible", true), | eq("visible", true), | ||||
eq("currency", currency), | |||||
or(isNull("code"), eq("code", code)))))); | or(isNull("code"), eq("code", code)))))); | ||||
} | } | ||||
} | } | ||||
public List<Promotion> findEnabledAndActiveWithReferral() { | |||||
return filterActive(findByFields("enabled", true, "referral", true, "adminAssignOnly", false)); | |||||
public List<Promotion> findEnabledAndActiveWithReferral(String currency) { | |||||
return filterActive(findByFields( | |||||
"enabled", true, | |||||
"referral", true, | |||||
"currency", currency, | |||||
"adminAssignOnly", false)); | |||||
} | } | ||||
public Promotion filterActive(Promotion promo) { return promo != null && promo.active() ? promo : null; } | public Promotion filterActive(Promotion promo) { return promo != null && promo.active() ? promo : null; } | ||||
@@ -98,6 +98,7 @@ public class AccountPayment extends IdentifiableBase implements HasAccountNoName | |||||
} | } | ||||
@Transient @Getter @Setter private transient Bill billObject; | @Transient @Getter @Setter private transient Bill billObject; | ||||
@Transient @Getter @Setter private transient AccountPaymentMethod paymentMethodObject; | |||||
public static int totalPayments (List<AccountPayment> payments) { | public static int totalPayments (List<AccountPayment> payments) { | ||||
return empty(payments) ? 0 : payments.stream().mapToInt(AccountPayment::getAmountInt).sum(); | return empty(payments) ? 0 : payments.stream().mapToInt(AccountPayment::getAmountInt).sum(); | ||||
@@ -12,6 +12,7 @@ import org.cobbzilla.wizard.model.entityconfig.annotations.*; | |||||
import org.hibernate.annotations.Type; | import org.hibernate.annotations.Type; | ||||
import javax.persistence.*; | import javax.persistence.*; | ||||
import java.util.List; | |||||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_LONG; | import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENCRYPTED_LONG; | ||||
import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_LONG; | import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_LONG; | ||||
@@ -74,5 +75,6 @@ public class Bill extends IdentifiableBase implements HasAccountNoName { | |||||
public boolean hasRefundedAmount () { return refundedAmount != null && refundedAmount > 0L; } | public boolean hasRefundedAmount () { return refundedAmount != null && refundedAmount > 0L; } | ||||
@Transient @Getter @Setter private transient BubblePlan planObject; | @Transient @Getter @Setter private transient BubblePlan planObject; | ||||
@Transient @Getter @Setter private transient List<AccountPayment> payments; | |||||
} | } |
@@ -114,6 +114,10 @@ public class Promotion extends IdentifiableBase | |||||
@ECIndex @Column(nullable=false, updatable=false, length=10) | @ECIndex @Column(nullable=false, updatable=false, length=10) | ||||
@Getter @Setter private String currency; | @Getter @Setter private String currency; | ||||
public boolean isCurrency(String currency) { | |||||
return currency != null && currency.equalsIgnoreCase(this.currency); | |||||
} | |||||
@ECSearchable @ECField(index=120) | @ECSearchable @ECField(index=120) | ||||
@ECIndex @Column(nullable=false, updatable=false) | @ECIndex @Column(nullable=false, updatable=false) | ||||
@Getter @Setter private Integer minValue = 100; | @Getter @Setter private Integer minValue = 100; | ||||
@@ -4,6 +4,7 @@ import bubble.dao.SessionDAO; | |||||
import bubble.dao.account.AccountDAO; | import bubble.dao.account.AccountDAO; | ||||
import bubble.dao.account.AccountPolicyDAO; | import bubble.dao.account.AccountPolicyDAO; | ||||
import bubble.dao.account.message.AccountMessageDAO; | import bubble.dao.account.message.AccountMessageDAO; | ||||
import bubble.dao.bill.BubblePlanDAO; | |||||
import bubble.dao.cloud.BubbleNodeDAO; | import bubble.dao.cloud.BubbleNodeDAO; | ||||
import bubble.model.CertType; | import bubble.model.CertType; | ||||
import bubble.model.account.*; | import bubble.model.account.*; | ||||
@@ -47,11 +48,13 @@ import static bubble.model.account.Account.validatePassword; | |||||
import static bubble.model.cloud.BubbleNetwork.TAG_ALLOW_REGISTRATION; | import static bubble.model.cloud.BubbleNetwork.TAG_ALLOW_REGISTRATION; | ||||
import static bubble.model.cloud.BubbleNetwork.TAG_PARENT_ACCOUNT; | import static bubble.model.cloud.BubbleNetwork.TAG_PARENT_ACCOUNT; | ||||
import static bubble.model.cloud.notify.NotificationType.retrieve_backup; | import static bubble.model.cloud.notify.NotificationType.retrieve_backup; | ||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
import static bubble.server.BubbleServer.getRestoreKey; | import static bubble.server.BubbleServer.getRestoreKey; | ||||
import static java.util.concurrent.TimeUnit.SECONDS; | import static java.util.concurrent.TimeUnit.SECONDS; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | import static org.cobbzilla.util.daemon.ZillaRuntime.*; | ||||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | ||||
import static org.cobbzilla.util.http.HttpContentTypes.CONTENT_TYPE_ANY; | import static org.cobbzilla.util.http.HttpContentTypes.CONTENT_TYPE_ANY; | ||||
import static org.cobbzilla.util.string.LocaleUtil.currencyForLocale; | |||||
import static org.cobbzilla.util.system.Sleep.sleep; | import static org.cobbzilla.util.system.Sleep.sleep; | ||||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | import static org.cobbzilla.wizard.resources.ResourceUtil.*; | ||||
@@ -70,6 +73,7 @@ public class AuthResource { | |||||
@Autowired private ActivationService activationService; | @Autowired private ActivationService activationService; | ||||
@Autowired private AccountMessageDAO accountMessageDAO; | @Autowired private AccountMessageDAO accountMessageDAO; | ||||
@Autowired private StandardAccountMessageService messageService; | @Autowired private StandardAccountMessageService messageService; | ||||
@Autowired private BubblePlanDAO planDAO; | |||||
@Autowired private BubbleNodeDAO nodeDAO; | @Autowired private BubbleNodeDAO nodeDAO; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AuthenticatorService authenticatorService; | @Autowired private AuthenticatorService authenticatorService; | ||||
@@ -197,8 +201,14 @@ public class AuthResource { | |||||
request.getContact().validate(errors); | request.getContact().validate(errors); | ||||
} | } | ||||
String currency = null; | |||||
if (configuration.paymentsEnabled()) { | if (configuration.paymentsEnabled()) { | ||||
errors.addAll(promoService.validatePromotions(request.getPromoCode())); | |||||
currency = currencyForLocale(request.getLocale(), getDEFAULT_LOCALE()); | |||||
// do we have any plans with this currency? | |||||
if (!planDAO.getSupportedCurrencies().contains(currency)) { | |||||
currency = currencyForLocale(getDEFAULT_LOCALE()); | |||||
} | |||||
errors.addAll(promoService.validatePromotions(request.getPromoCode(), currency)); | |||||
} | } | ||||
if (errors.isInvalid()) return invalid(errors); | if (errors.isInvalid()) return invalid(errors); | ||||
@@ -211,7 +221,7 @@ public class AuthResource { | |||||
SimpleViolationException promoEx = null; | SimpleViolationException promoEx = null; | ||||
if (configuration.paymentsEnabled()) { | if (configuration.paymentsEnabled()) { | ||||
try { | try { | ||||
promoService.applyPromotions(account, request.getPromoCode()); | |||||
promoService.applyPromotions(account, request.getPromoCode(), currency); | |||||
} catch (SimpleViolationException e) { | } catch (SimpleViolationException e) { | ||||
promoEx = e; | promoEx = e; | ||||
} | } | ||||
@@ -6,14 +6,16 @@ import bubble.dao.account.AccountSshKeyDAO; | |||||
import bubble.dao.bill.AccountPaymentMethodDAO; | import bubble.dao.bill.AccountPaymentMethodDAO; | ||||
import bubble.dao.bill.AccountPlanDAO; | import bubble.dao.bill.AccountPlanDAO; | ||||
import bubble.dao.bill.BubblePlanDAO; | import bubble.dao.bill.BubblePlanDAO; | ||||
import bubble.dao.bill.PromotionDAO; | |||||
import bubble.dao.cloud.BubbleDomainDAO; | import bubble.dao.cloud.BubbleDomainDAO; | ||||
import bubble.dao.cloud.BubbleFootprintDAO; | import bubble.dao.cloud.BubbleFootprintDAO; | ||||
import bubble.dao.cloud.BubbleNetworkDAO; | import bubble.dao.cloud.BubbleNetworkDAO; | ||||
import bubble.dao.cloud.CloudServiceDAO; | import bubble.dao.cloud.CloudServiceDAO; | ||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.account.AccountSshKey; | import bubble.model.account.AccountSshKey; | ||||
import bubble.model.bill.*; | |||||
import bubble.model.bill.AccountPaymentMethod; | |||||
import bubble.model.bill.AccountPlan; | |||||
import bubble.model.bill.BubblePlan; | |||||
import bubble.model.bill.PaymentMethodType; | |||||
import bubble.model.cloud.BubbleDomain; | import bubble.model.cloud.BubbleDomain; | ||||
import bubble.model.cloud.BubbleFootprint; | import bubble.model.cloud.BubbleFootprint; | ||||
import bubble.model.cloud.BubbleNetwork; | import bubble.model.cloud.BubbleNetwork; | ||||
@@ -56,7 +58,6 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
@Autowired private AuthenticatorService authenticatorService; | @Autowired private AuthenticatorService authenticatorService; | ||||
@Autowired private GeoService geoService; | @Autowired private GeoService geoService; | ||||
@Autowired private PromotionDAO promotionDAO; | |||||
public AccountPlansResource(Account account) { super(account); } | public AccountPlansResource(Account account) { super(account); } | ||||
@@ -1,16 +1,10 @@ | |||||
package bubble.resources.bill; | package bubble.resources.bill; | ||||
import bubble.cloud.payment.PaymentServiceDriver; | import bubble.cloud.payment.PaymentServiceDriver; | ||||
import bubble.dao.bill.AccountPaymentMethodDAO; | |||||
import bubble.dao.bill.AccountPlanDAO; | |||||
import bubble.dao.bill.BillDAO; | |||||
import bubble.dao.bill.BubblePlanDAO; | |||||
import bubble.dao.bill.*; | |||||
import bubble.dao.cloud.CloudServiceDAO; | import bubble.dao.cloud.CloudServiceDAO; | ||||
import bubble.model.account.Account; | import bubble.model.account.Account; | ||||
import bubble.model.bill.AccountPaymentMethod; | |||||
import bubble.model.bill.AccountPlan; | |||||
import bubble.model.bill.Bill; | |||||
import bubble.model.bill.BubblePlan; | |||||
import bubble.model.bill.*; | |||||
import bubble.model.cloud.CloudService; | import bubble.model.cloud.CloudService; | ||||
import bubble.resources.account.ReadOnlyAccountOwnedResource; | import bubble.resources.account.ReadOnlyAccountOwnedResource; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
@@ -30,14 +24,18 @@ import java.util.Map; | |||||
import static bubble.ApiConstants.EP_PAY; | import static bubble.ApiConstants.EP_PAY; | ||||
import static bubble.ApiConstants.EP_PAYMENTS; | import static bubble.ApiConstants.EP_PAYMENTS; | ||||
import static org.cobbzilla.util.http.URIUtil.queryParams; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | import static org.cobbzilla.wizard.resources.ResourceUtil.*; | ||||
@Slf4j | @Slf4j | ||||
public class BillsResource extends ReadOnlyAccountOwnedResource<Bill, BillDAO> { | public class BillsResource extends ReadOnlyAccountOwnedResource<Bill, BillDAO> { | ||||
public static final String PARAM_PAYMENTS = "payments"; | |||||
@Autowired private BubblePlanDAO planDAO; | @Autowired private BubblePlanDAO planDAO; | ||||
@Autowired private AccountPlanDAO accountPlanDAO; | @Autowired private AccountPlanDAO accountPlanDAO; | ||||
@Autowired private AccountPaymentMethodDAO paymentMethodDAO; | @Autowired private AccountPaymentMethodDAO paymentMethodDAO; | ||||
@Autowired private AccountPaymentDAO paymentDAO; | |||||
@Autowired private CloudServiceDAO cloudDAO; | @Autowired private CloudServiceDAO cloudDAO; | ||||
private AccountPlan accountPlan; | private AccountPlan accountPlan; | ||||
@@ -51,7 +49,24 @@ public class BillsResource extends ReadOnlyAccountOwnedResource<Bill, BillDAO> { | |||||
@Override protected Bill find(ContainerRequest ctx, String id) { | @Override protected Bill find(ContainerRequest ctx, String id) { | ||||
final Bill bill = super.find(ctx, id); | final Bill bill = super.find(ctx, id); | ||||
return bill == null || (accountPlan != null && !bill.getAccountPlan().equals(accountPlan.getUuid())) ? null : bill; | |||||
if (bill == null || (accountPlan != null && !bill.getAccountPlan().equals(accountPlan.getUuid()))) return null; | |||||
final Map<String, String> params = queryParams(ctx.getRequestUri().getQuery()); | |||||
if (Boolean.parseBoolean(params.get(PARAM_PAYMENTS))) { | |||||
final List<AccountPayment> payments = paymentDAO.findByAccountAndAccountPlanAndBill(bill.getAccount(), bill.getAccountPlan(), bill.getUuid()); | |||||
for (AccountPayment payment : payments) { | |||||
final String paymentMethodUuid = payment.getPaymentMethod(); | |||||
payment.setPaymentMethodObject(findPaymentMethod(paymentMethodUuid)); | |||||
} | |||||
return bill.setPayments(payments); | |||||
} | |||||
return bill; | |||||
} | |||||
private Map<String, AccountPaymentMethod> paymentMethodCache = new ExpirationMap<>(ExpirationEvictionPolicy.atime); | |||||
private AccountPaymentMethod findPaymentMethod(String paymentMethodUuid) { | |||||
return paymentMethodCache.computeIfAbsent(paymentMethodUuid, k -> paymentMethodDAO.findByUuid(k)); | |||||
} | } | ||||
@Override protected List<Bill> list(ContainerRequest ctx) { | @Override protected List<Bill> list(ContainerRequest ctx) { | ||||
@@ -64,7 +79,7 @@ public class BillsResource extends ReadOnlyAccountOwnedResource<Bill, BillDAO> { | |||||
} | } | ||||
private Map<String, BubblePlan> planCache = new ExpirationMap<>(ExpirationEvictionPolicy.atime); | private Map<String, BubblePlan> planCache = new ExpirationMap<>(ExpirationEvictionPolicy.atime); | ||||
private BubblePlan findPlan(String planUuid) { return planCache.computeIfAbsent(planUuid, k -> planDAO.findByUuid(planUuid)); } | |||||
private BubblePlan findPlan(String planUuid) { return planCache.computeIfAbsent(planUuid, k -> planDAO.findByUuid(k)); } | |||||
@Path("/{id}"+EP_PAYMENTS) | @Path("/{id}"+EP_PAYMENTS) | ||||
public AccountPaymentsResource getPayments(@Context ContainerRequest ctx, | public AccountPaymentsResource getPayments(@Context ContainerRequest ctx, | ||||
@@ -18,7 +18,10 @@ import javax.ws.rs.core.Context; | |||||
import javax.ws.rs.core.Response; | import javax.ws.rs.core.Response; | ||||
import static bubble.ApiConstants.PROMOTIONS_ENDPOINT; | import static bubble.ApiConstants.PROMOTIONS_ENDPOINT; | ||||
import static bubble.server.BubbleConfiguration.getDEFAULT_LOCALE; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | ||||
import static org.cobbzilla.util.string.LocaleUtil.currencyForLocale; | |||||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | import static org.cobbzilla.wizard.resources.ResourceUtil.*; | ||||
@Consumes(APPLICATION_JSON) | @Consumes(APPLICATION_JSON) | ||||
@@ -35,12 +38,14 @@ public class PromotionsResource { | |||||
@GET | @GET | ||||
public Response listPromos(@Context ContainerRequest ctx, | public Response listPromos(@Context ContainerRequest ctx, | ||||
@QueryParam("currency") String currency, | |||||
@QueryParam("code") String code) { | @QueryParam("code") String code) { | ||||
if (empty(currency)) currency = currencyForLocale(getDEFAULT_LOCALE()); | |||||
final Account caller = optionalUserPrincipal(ctx); | final Account caller = optionalUserPrincipal(ctx); | ||||
if (caller != null && caller.admin()) { | if (caller != null && caller.admin()) { | ||||
return ok(promotionDAO.findAll()); | return ok(promotionDAO.findAll()); | ||||
} | } | ||||
return ok(promotionDAO.findVisibleAndEnabledAndActiveWithNoCodeOrWithCode(code)); | |||||
return ok(promotionDAO.findVisibleAndEnabledAndActiveWithNoCodeOrWithCode(code, currency)); | |||||
} | } | ||||
@GET @Path("/{id}") | @GET @Path("/{id}") | ||||
@@ -42,19 +42,19 @@ public class PromotionService { | |||||
@Autowired protected AccountPaymentMethodDAO accountPaymentMethodDAO; | @Autowired protected AccountPaymentMethodDAO accountPaymentMethodDAO; | ||||
@Autowired private BubbleConfiguration configuration; | @Autowired private BubbleConfiguration configuration; | ||||
public void applyPromotions(Account account, String code) { | |||||
public void applyPromotions(Account account, String code, String currency) { | |||||
// apply promo code (or default) promotion | // apply promo code (or default) promotion | ||||
final Set<Promotion> promos = new TreeSet<>(); | final Set<Promotion> promos = new TreeSet<>(); | ||||
ReferralCode referralCode = null; | ReferralCode referralCode = null; | ||||
if (!empty(code)) { | if (!empty(code)) { | ||||
Promotion promo = promotionDAO.findEnabledAndActiveWithCode(code); | |||||
Promotion promo = promotionDAO.findEnabledAndActiveWithCode(code, currency); | |||||
if (promo == null) { | if (promo == null) { | ||||
// check referral codes | // check referral codes | ||||
// it might be a referral code | // it might be a referral code | ||||
referralCode = referralCodeDAO.findByName(code); | referralCode = referralCodeDAO.findByName(code); | ||||
if (referralCode != null && !referralCode.claimed()) { | if (referralCode != null && !referralCode.claimed()) { | ||||
// is there a referral promotion we can use? | // is there a referral promotion we can use? | ||||
for (Promotion p : promotionDAO.findEnabledAndActiveWithReferral()) { | |||||
for (Promotion p : promotionDAO.findEnabledAndActiveWithReferral(currency)) { | |||||
promos.add(p); | promos.add(p); | ||||
break; | break; | ||||
} | } | ||||
@@ -66,7 +66,7 @@ public class PromotionService { | |||||
} | } | ||||
// everyone gets the highest-priority default promotion, if there are any enabled and active | // everyone gets the highest-priority default promotion, if there are any enabled and active | ||||
for (Promotion p : promotionDAO.findEnabledAndActiveWithNoCode()) { | |||||
for (Promotion p : promotionDAO.findEnabledAndActiveWithNoCode(currency)) { | |||||
promos.add(p); | promos.add(p); | ||||
break; | break; | ||||
} | } | ||||
@@ -105,9 +105,9 @@ public class PromotionService { | |||||
} | } | ||||
} | } | ||||
public ValidationResult validatePromotions(String code) { | |||||
public ValidationResult validatePromotions(String code, String currency) { | |||||
if (!empty(code)) { | if (!empty(code)) { | ||||
Promotion promo = promotionDAO.findEnabledAndActiveWithCode(code); | |||||
Promotion promo = promotionDAO.findEnabledAndActiveWithCode(code, currency); | |||||
if (promo == null) { | if (promo == null) { | ||||
// it might be a referral code | // it might be a referral code | ||||
final ReferralCode referralCode = referralCodeDAO.findByName(code); | final ReferralCode referralCode = referralCodeDAO.findByName(code); | ||||
@@ -116,7 +116,7 @@ public class PromotionService { | |||||
if (referer == null || referer.deleted()) return new ValidationResult("err.promoCode.notFound"); | if (referer == null || referer.deleted()) return new ValidationResult("err.promoCode.notFound"); | ||||
// is there a referral promotion we can use? | // is there a referral promotion we can use? | ||||
for (Promotion p : promotionDAO.findEnabledAndActiveWithReferral()) { | |||||
for (Promotion p : promotionDAO.findEnabledAndActiveWithReferral(currency)) { | |||||
// todo: add JS check? | // todo: add JS check? | ||||
promo = p; | promo = p; | ||||
break; | break; | ||||
@@ -167,11 +167,15 @@ public class PromotionService { | |||||
final CloudService promoCloud = cloudDAO.findByUuid(promo.getCloud()); | final CloudService promoCloud = cloudDAO.findByUuid(promo.getCloud()); | ||||
final String prefix = getClass().getSimpleName()+": "; | final String prefix = getClass().getSimpleName()+": "; | ||||
if (promoCloud == null) { | if (promoCloud == null) { | ||||
reportError(prefix+"purchase: cloud "+promo.getCloud()+" not found for promotion "+promo.getUuid()); | |||||
reportError(prefix+"purchase: cloud "+promo.getCloud()+" not found for promotion "+promo.getName()); | |||||
continue; | continue; | ||||
} | } | ||||
if (promoCloud.getType() != CloudServiceType.payment) { | if (promoCloud.getType() != CloudServiceType.payment) { | ||||
reportError(prefix+"purchase: cloud "+promo.getCloud()+" for promotion "+promo.getUuid()+" has wrong type (expected 'payment'): "+promoCloud.getType()); | |||||
reportError(prefix+"purchase: cloud "+promo.getCloud()+" for promotion "+promo.getName()+" has wrong type (expected 'payment'): "+promoCloud.getType()); | |||||
continue; | |||||
} | |||||
if (!promo.getCurrency().equals(plan.getCurrency())) { | |||||
reportError(prefix+"purchase: promotion "+promo.getName()+" has wrong currency (expected "+plan.getCurrency()+" for plan "+plan.getName()+"): "+promoCloud.getType()); | |||||
continue; | continue; | ||||
} | } | ||||
log.info("purchase: using Promotion: "+promo.getName()); | log.info("purchase: using Promotion: "+promo.getName()); | ||||
@@ -242,10 +242,48 @@ label_bill_status=Status | |||||
bill_status_paid=Paid | bill_status_paid=Paid | ||||
bill_status_unpaid=Unpaid | bill_status_unpaid=Unpaid | ||||
bill_status_partial_payment=Partial payment | bill_status_partial_payment=Partial payment | ||||
label_bill_period=Period | |||||
bill_status_undefined=Unknown | |||||
bill_status_null=Unknown | |||||
bill_status_=Unknown | |||||
label_bill_period=Date | |||||
label_bill_period_start=From | |||||
label_bill_period_end=To | |||||
label_bill_total=Amount | label_bill_total=Amount | ||||
label_bill_total_format={{messages['currency_symbol_'+currency.toUpperCase()]}}{{totalMajorUnits}}{{totalMinorUnits === 0 ? '' : totalMinorUnits < 10 ? '.0'+totalMinorUnits : '.'+totalMinorUnits}} | label_bill_total_format={{messages['currency_symbol_'+currency.toUpperCase()]}}{{totalMajorUnits}}{{totalMinorUnits === 0 ? '' : totalMinorUnits < 10 ? '.0'+totalMinorUnits : '.'+totalMinorUnits}} | ||||
label_bill_refunded=refunded | label_bill_refunded=refunded | ||||
label_payment_type=Type | |||||
label_payment_method=Paid By | |||||
label_payment_status=Status | |||||
label_payment_amount=Amount | |||||
label_payment_amount_format={{messages['currency_symbol_'+currency.toUpperCase()]}}{{amountMajorUnits}}{{amountMinorUnits === 0 ? '' : amountMinorUnits < 10 ? '.0'+amountMinorUnits : '.'+amountMinorUnits}} | |||||
label_payment_action=Action | |||||
button_label_close_bill_detail=Close | |||||
payment_method_credit=Credit/Debit Card | |||||
payment_method_code=Invitation Code | |||||
payment_method_free=Free! | |||||
payment_method_promotional_credit=Promotion | |||||
payment_method_undefined= | |||||
payment_method_null= | |||||
payment_method_= | |||||
payment_status_init=Created | |||||
payment_status_success=Success | |||||
payment_status_failure=Failure | |||||
payment_status_unknown=Unknown | |||||
payment_status_undefined=Unknown | |||||
payment_status_null=Unknown | |||||
payment_status_=Unknown | |||||
payment_type_payment=payment | |||||
payment_type_credit_applied=credit applied | |||||
payment_type_refund=refund | |||||
label_promotion_FirstMonthFree=First Month Free | |||||
label_promotion_ReferralMonthFree=Referral Bonus | |||||
label_promotion_AccountCredit1=$1 Bonus | |||||
label_promotion_AccountCredit5=$5 Bonus | |||||
label_promotion_AccountCreditBill=Full Bill Bonus ($100 max value) | |||||
# Bubble Plans | # Bubble Plans | ||||
plan_name_bubble=Bubble Standard | plan_name_bubble=Bubble Standard | ||||
@@ -1 +1 @@ | |||||
Subproject commit c194f2c575af7d7735759759ee3dbfda5c768a15 | |||||
Subproject commit ddbe7e69b39138c458766c82ee616c31fa7980d1 |
@@ -1 +1 @@ | |||||
Subproject commit d1d485b1a8dcd51da565ca21886a95a728f3a832 | |||||
Subproject commit 77831c8f23574ebdc8476dd835f8bbfbd8404338 |