diff --git a/bubble-server/src/main/java/bubble/cloud/payment/PaymentDriverBase.java b/bubble-server/src/main/java/bubble/cloud/payment/PaymentDriverBase.java index c250d8a6..4d36bbe0 100644 --- a/bubble-server/src/main/java/bubble/cloud/payment/PaymentDriverBase.java +++ b/bubble-server/src/main/java/bubble/cloud/payment/PaymentDriverBase.java @@ -85,7 +85,7 @@ public abstract class PaymentDriverBase extends CloudServiceDriverBase imp final AccountPlanPayment priorPayment = findPriorPayment(plan, paymentMethod, bill); if (priorPayment != null) { - billDAO.update(bill.setStatus(BillStatus.paid)); + billDAO.update(bill.setPrice(0L).setStatus(BillStatus.paid)); accountPlanPaymentDAO.create(new AccountPlanPayment() .setAccount(accountPlan.getAccount()) .setPlan(accountPlan.getPlan()) @@ -129,14 +129,15 @@ public abstract class PaymentDriverBase extends CloudServiceDriverBase imp // - it is for the current period // - the corresponding AccountPlan has been deleted // if so we can re-use that payment and do not need to charge anything now - final List priorSimilarPayments = accountPlanPaymentDAO.findByAccountPaymentMethodAndPeriodAndPriceAndCurrency( + final List priorSimilarPayments = accountPlanPaymentDAO.findByAccountPaymentMethodAndPeriodAndCurrency( paymentMethod.getUuid(), bill.getPeriod(), - plan.getPrice(), plan.getCurrency()); for (AccountPlanPayment app : priorSimilarPayments) { final AccountPlan ap = accountPlanDAO.findByUuid(app.getAccountPlan()); - if (ap != null && ap.deleted()) return app; + if (ap != null && ap.deleted() && app.getAmount() >= plan.getPrice()) { + return app; + } } return null; } diff --git a/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java b/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java index 0cbe64a7..57d0f602 100644 --- a/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java +++ b/bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java @@ -2,13 +2,18 @@ package bubble.dao.bill; import bubble.cloud.payment.PaymentServiceDriver; import bubble.dao.account.AccountOwnedEntityDAO; +import bubble.dao.cloud.BubbleNetworkDAO; import bubble.dao.cloud.CloudServiceDAO; import bubble.model.bill.*; +import bubble.model.cloud.BubbleNetwork; +import bubble.model.cloud.BubbleNetworkState; import bubble.model.cloud.CloudService; import bubble.server.BubbleConfiguration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.List; + import static java.util.concurrent.TimeUnit.SECONDS; import static org.cobbzilla.util.daemon.ZillaRuntime.background; import static org.cobbzilla.util.system.Sleep.sleep; @@ -21,12 +26,17 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO { @Autowired private BubblePlanDAO planDAO; @Autowired private BillDAO billDAO; @Autowired private CloudServiceDAO cloudDAO; + @Autowired private BubbleNetworkDAO networkDAO; @Autowired private BubbleConfiguration configuration; public AccountPlan findByAccountAndNetwork(String accountUuid, String networkUuid) { return findByUniqueFields("account", accountUuid, "network", networkUuid); } + public List findByAccountAndNotDeleted(String account) { + return findByFields("account", account, "deleted", false); + } + @Override public Object preCreate(AccountPlan accountPlan) { if (!accountPlan.hasPaymentMethod()) throw invalidEx("err.paymentMethod.required"); @@ -74,4 +84,17 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO { return super.postCreate(accountPlan, context); } + @Override public void delete(String uuid) { + final AccountPlan accountPlan = findByUuid(uuid); + if (accountPlan == null) return; + + final BubbleNetwork network = networkDAO.findByUuid(accountPlan.getNetwork()); + if (network != null && network.getState() != BubbleNetworkState.stopped) { + throw invalidEx("err.accountPlan.stopNetworkBeforeDeleting"); + } + update(accountPlan.setDeleted(true).setEnabled(false)); + } + + @Override public void forceDelete(String uuid) { super.delete(uuid); } + } diff --git a/bubble-server/src/main/java/bubble/dao/bill/AccountPlanPaymentDAO.java b/bubble-server/src/main/java/bubble/dao/bill/AccountPlanPaymentDAO.java index 040a211d..158a224a 100644 --- a/bubble-server/src/main/java/bubble/dao/bill/AccountPlanPaymentDAO.java +++ b/bubble-server/src/main/java/bubble/dao/bill/AccountPlanPaymentDAO.java @@ -6,8 +6,6 @@ import org.springframework.stereotype.Repository; import java.util.List; -import static org.hibernate.criterion.Restrictions.*; - @Repository public class AccountPlanPaymentDAO extends AccountOwnedEntityDAO { @@ -17,16 +15,14 @@ public class AccountPlanPaymentDAO extends AccountOwnedEntityDAO findByAccountPaymentMethodAndPeriodAndPriceAndCurrency(String paymentMethodUuid, - String billPeriod, - Long price, - String currency) { - return list(criteria().add(and( - eq("paymentMethod", paymentMethodUuid), - eq("period", billPeriod), - eq("currency", currency), - ge("amount", price) - ))); + public List findByAccountPayment(String accountPayment) { + return findByField("payment", accountPayment); + } + + public List findByAccountPaymentMethodAndPeriodAndCurrency(String paymentMethodUuid, + String billPeriod, + String currency) { + return findByFields("paymentMethod", paymentMethodUuid, "period", billPeriod, "currency", currency); } public List findByAccountAndBill(String accountUuid, String billUuid) { @@ -40,4 +36,5 @@ public class AccountPlanPaymentDAO extends AccountOwnedEntityDAO findByAccountAndAccountPlanAndBill(String accountUuid, String accountPlanUuid, String billUuid) { return findByFields("account", accountUuid, "accountPlan", accountPlanUuid, "bill", billUuid); } + } diff --git a/bubble-server/src/main/java/bubble/dao/cloud/BubbleNetworkDAO.java b/bubble-server/src/main/java/bubble/dao/cloud/BubbleNetworkDAO.java index 0a3e26a6..fbef0d53 100644 --- a/bubble-server/src/main/java/bubble/dao/cloud/BubbleNetworkDAO.java +++ b/bubble-server/src/main/java/bubble/dao/cloud/BubbleNetworkDAO.java @@ -3,7 +3,10 @@ package bubble.dao.cloud; import bubble.dao.account.AccountOwnedEntityDAO; import bubble.dao.bill.AccountPlanDAO; import bubble.model.bill.AccountPlan; -import bubble.model.cloud.*; +import bubble.model.cloud.BubbleDomain; +import bubble.model.cloud.BubbleNetwork; +import bubble.model.cloud.BubbleNode; +import bubble.model.cloud.CloudService; import bubble.server.BubbleConfiguration; import bubble.service.boot.SelfNodeService; import bubble.service.cloud.NetworkService; @@ -16,8 +19,6 @@ import java.io.IOException; import java.util.List; import java.util.stream.Collectors; -import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; - @Repository @Slf4j public class BubbleNetworkDAO extends AccountOwnedEntityDAO { @@ -64,12 +65,11 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO { public void delete(String uuid, boolean force) { final BubbleNetwork network = findByUuid(uuid); - if (network.getState() != BubbleNetworkState.stopped) { - if (force) { - networkService.stopNetwork(network); - } else { - throw invalidEx("err.network.cannotDelete", "stop network before deleting"); - } + if (network == null) return; + try { + networkService.stopNetwork(network); + } catch (Exception e) { + log.warn("delete("+uuid+"): error stopping network: "+e); } try { if (force) { @@ -104,11 +104,10 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO { if (accountPlan == null) { log.warn("delete("+uuid+"): AccountPlan not found"); } else { - if (force) { - accountPlanDAO.delete(accountPlan.getUuid()); - } else { - accountPlanDAO.update(accountPlan.setNetwork(null).setDeletedNetwork(network.getUuid())); - } + accountPlanDAO.update(accountPlan + .setEnabled(false) + .setNetwork(null) + .setDeletedNetwork(network.getUuid())); } if (force) { diff --git a/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java b/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java index daf13f31..03c72346 100644 --- a/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java +++ b/bubble-server/src/main/java/bubble/model/bill/AccountPaymentMethod.java @@ -23,10 +23,7 @@ import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; import org.cobbzilla.wizard.validation.ValidationResult; import org.hibernate.annotations.Type; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; +import javax.persistence.*; import static org.cobbzilla.util.daemon.ZillaRuntime.empty; import static org.cobbzilla.util.reflect.ReflectionUtil.copy; diff --git a/bubble-server/src/main/java/bubble/model/bill/AccountPlan.java b/bubble-server/src/main/java/bubble/model/bill/AccountPlan.java index 2deb883f..68a1f8ac 100644 --- a/bubble-server/src/main/java/bubble/model/bill/AccountPlan.java +++ b/bubble-server/src/main/java/bubble/model/bill/AccountPlan.java @@ -31,7 +31,8 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy; public class AccountPlan extends IdentifiableBase implements HasAccount { public static final String[] CREATE_FIELDS = { - "name", "description", "locale", "timezone", "domain", "network", "plan", "footprint", "paymentMethod" + "name", "description", "locale", "timezone", "domain", "network", "plan", "footprint", + "paymentMethod", "savedPaymentMethod" }; @SuppressWarnings("unused") @@ -62,9 +63,14 @@ public class AccountPlan extends IdentifiableBase implements HasAccount { @Getter @Setter private Boolean enabled = false; public boolean enabled() { return enabled != null && enabled; } + @Column(nullable=false) + @Getter @Setter private Boolean deleted = false; + public boolean deleted() { return deleted != null && deleted; } + public boolean notDeleted() { return !deleted(); } + @ECIndex(unique=true) @Column(length=UUID_MAXLEN) @Getter @Setter private String deletedNetwork; - public boolean deleted() { return deletedNetwork != null; } + public boolean hasDeletedNetwork() { return deletedNetwork != null; } // Fields below are used when creating a new plan, to also create the network associated with it @Size(max=10000, message="err.description.length") diff --git a/bubble-server/src/main/java/bubble/model/bill/Bill.java b/bubble-server/src/main/java/bubble/model/bill/Bill.java index e6b2d6ad..577e05f2 100644 --- a/bubble-server/src/main/java/bubble/model/bill/Bill.java +++ b/bubble-server/src/main/java/bubble/model/bill/Bill.java @@ -50,7 +50,7 @@ public class Bill extends IdentifiableBase implements HasAccountNoName { @Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") @Getter @Setter private Long quantity = 0L; - @Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") + @Type(type=ENCRYPTED_LONG) @Column(columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") @Getter @Setter private Long price = 0L; @ECIndex @Column(nullable=false, updatable=false, length=10) diff --git a/bubble-server/src/main/java/bubble/resources/bill/AccountPaymentsResource.java b/bubble-server/src/main/java/bubble/resources/bill/AccountPaymentsResource.java index 96d43f2c..2132f155 100644 --- a/bubble-server/src/main/java/bubble/resources/bill/AccountPaymentsResource.java +++ b/bubble-server/src/main/java/bubble/resources/bill/AccountPaymentsResource.java @@ -1,21 +1,55 @@ package bubble.resources.bill; import bubble.dao.bill.AccountPaymentDAO; +import bubble.dao.bill.AccountPlanPaymentDAO; +import bubble.dao.bill.BillDAO; import bubble.model.account.Account; import bubble.model.bill.AccountPayment; +import bubble.model.bill.AccountPlanPayment; +import bubble.model.bill.Bill; import bubble.resources.account.ReadOnlyAccountOwnedResource; import lombok.extern.slf4j.Slf4j; +import org.glassfish.jersey.server.ContainerRequest; +import org.springframework.beans.factory.annotation.Autowired; -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.List; +import static bubble.ApiConstants.EP_BILLS; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; +import static org.cobbzilla.wizard.resources.ResourceUtil.notFound; +import static org.cobbzilla.wizard.resources.ResourceUtil.ok; @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @Slf4j public class AccountPaymentsResource extends ReadOnlyAccountOwnedResource { + @Autowired private AccountPlanPaymentDAO planPaymentDAO; + @Autowired private BillDAO billDAO; + public AccountPaymentsResource(Account account) { super(account); } + @GET @Path("/{id}"+EP_BILLS) + public Response getBills(@Context ContainerRequest ctx, + @PathParam("id") String id) { + final AccountPayment accountPayment = super.find(ctx, id); + if (accountPayment == null) return notFound(id); + + final List planPayments = planPaymentDAO.findByAccountPayment(accountPayment.getUuid()); + final List bills = new ArrayList<>(); + for (AccountPlanPayment app : planPayments) { + final Bill bill = billDAO.findByUuid(app.getBill()); + if (bill == null) { + log.warn("getBills: bill "+app.getBill()+" not found for AccountPlanPayment="+app.getUuid()); + continue; + } + bills.add(bill); + } + return ok(bills); + } + } diff --git a/bubble-server/src/main/java/bubble/resources/bill/AccountPlanPaymentsResource.java b/bubble-server/src/main/java/bubble/resources/bill/AccountPlanPaymentsResource.java index 059e8a1e..aa864b4c 100644 --- a/bubble-server/src/main/java/bubble/resources/bill/AccountPlanPaymentsResource.java +++ b/bubble-server/src/main/java/bubble/resources/bill/AccountPlanPaymentsResource.java @@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j; import org.glassfish.jersey.server.ContainerRequest; import org.springframework.beans.factory.annotation.Autowired; +import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; @@ -75,7 +76,7 @@ public class AccountPlanPaymentsResource extends ReadOnlyAccountOwnedResource list(ContainerRequest ctx) { + return getDao().findByAccountAndNotDeleted(account.getUuid()); } @Override protected boolean canCreate(Request req, ContainerRequest ctx, Account caller, AccountPlan request) { @@ -166,6 +143,8 @@ public class AccountPlansResource extends AccountOwnedResource