@TimedResource @POST @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON) @ApiOperation(value = "Trigger an invoice generation", response = InvoiceJson.class) @ApiResponses(value = {@ApiResponse(code = 201, message = "Created invoice successfully"), @ApiResponse(code = 400, message = "Invalid account id or target datetime supplied")}) public Response createFutureInvoice(@ApiParam(required=true) @QueryParam(QUERY_ACCOUNT_ID) final UUID accountId, @QueryParam(QUERY_TARGET_DATE) final String targetDate, @HeaderParam(HDR_CREATED_BY) final String createdBy, @HeaderParam(HDR_REASON) final String reason, @HeaderParam(HDR_COMMENT) final String comment, @javax.ws.rs.core.Context final HttpServletRequest request, @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException { final CallContext callContext = context.createCallContextWithAccountId(accountId, createdBy, reason, comment, request); final LocalDate inputDate = toLocalDate(targetDate); try { final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(accountId, inputDate, callContext); return uriBuilder.buildResponse(uriInfo, InvoiceResource.class, "getInvoice", generatedInvoice.getId(), request); } catch (InvoiceApiException e) { if (e.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) { return Response.status(Status.NOT_FOUND).build(); } throw e; } }
public PaymentApiException(final InvoiceApiException e) { super(e, e.getCode(), e.getMessage()); }
@Override protected InvoiceApiException generateAlreadyExistsException(final InvoiceModelDao entity, final InternalCallContext context) { return new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, entity.getId()); }
return null; } catch (final InvoiceApiException e) { if (e.getCode() == ErrorCode.INVOICE_PLUGIN_API_ABORTED.getCode()) { return null; if (e.getCode() == ErrorCode.UNEXPECTED_ERROR.getCode() && !isDryRun) { log.warn("Illegal invoicing state detected for accountId='{}', dryRunArguments='{}', parking account", accountId, dryRunArguments, e); parkAccount(accountId, context); parkAccount(accountId, context); throw new InvoiceApiException(ErrorCode.UNEXPECTED_ERROR, "Failed to retrieve future notifications from notificationQ");
@Override public Void apply(@Nullable final Void input) { try { invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoice.getId(), invoice.getInvoiceItems().get(itemNb - 1).getId(), invoice.getInvoiceDate(), null, null, null, callContext); } catch (final InvoiceApiException e) { fail(e.toString()); } return null; } }, events);
@Override public Void apply(@Nullable final Void input) { try { for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) { invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoice.getId(), invoiceItem.getId(), invoice.getInvoiceDate(), null, null, null, callContext); } } catch (final InvoiceApiException e) { fail(e.toString()); } return null; } }, events);
@Override public void write(final OutputStream output) throws IOException, WebApplicationException { try { final JsonGenerator generator = mapper.getFactory().createGenerator(output); generator.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false); generator.writeStartObject(); while (iterator.hasNext()) { final Tag tag = iterator.next(); final UUID accountId = tag.getObjectId(); try { invoiceUserApi.triggerInvoiceGeneration(accountId, clock.getUTCToday(), callContext); generator.writeStringField(accountId.toString(), OK); } catch (final InvoiceApiException e) { if (e.getCode() != ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) { log.warn("Unable to trigger invoice generation for accountId='{}'", accountId); } generator.writeStringField(accountId.toString(), ErrorCode.fromCode(e.getCode()).toString()); } } generator.writeEndObject(); generator.close(); } finally { // In case the client goes away (IOException), make sure to close the underlying DB connection tags.close(); } } };
Assert.fail(); } catch (final InvoiceApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode()); Assert.assertTrue(e.getCause().getMessage().startsWith("Double billing detected")); Assert.fail(); } catch (final InvoiceApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode()); Assert.assertTrue(e.getCause().getMessage().startsWith("Double billing detected")); Assert.fail(); } catch (final InvoiceApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode()); Assert.assertTrue(e.getCause().getMessage().startsWith("Double billing detected"));
public PaymentApiException(final InvoiceApiException e) { super(e, e.getCode(), e.getMessage()); }
private Invoice processSubscriptionInternal(final UUID subscriptionId, final LocalDate targetDate, final boolean dryRunForNotification, final boolean isRescheduled, final InternalCallContext context) throws InvoiceApiException { try { if (subscriptionId == null) { log.warn("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION)); return null; } final UUID accountId = subscriptionApi.getAccountIdFromSubscriptionId(subscriptionId, context); final DryRunArguments dryRunArguments = dryRunForNotification ? TARGET_DATE_DRY_RUN_ARGUMENTS : null; return processAccountFromNotificationOrBusEvent(accountId, targetDate, dryRunArguments, isRescheduled, context); } catch (final SubscriptionBaseApiException e) { log.warn("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString())); return null; } }
@Test(groups = "slow") public void testCreateRefundOnNonExistingPayment() throws Exception { try { invoiceDao.createRefund(UUID.randomUUID(), BigDecimal.TEN, false, ImmutableMap.<UUID, BigDecimal>of(), null, context); Assert.fail(); } catch (InvoiceApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.INVOICE_PAYMENT_BY_ATTEMPT_NOT_FOUND.getCode()); } }
public void handleParentInvoiceCommitmentEvent(final UUID invoiceId, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) { try { final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Commit Invoice", CallOrigin.INTERNAL, UserType.SYSTEM, userToken); invoiceApi.commitInvoice(invoiceId, context); } catch (final InvoiceApiException e) { // In case we commit parent invoice earlier we expect to see an INVOICE_INVALID_STATUS status if (ErrorCode.INVOICE_INVALID_STATUS.getCode() != e.getCode()) { log.error(e.getMessage()); } } }
@Override public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception { final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class); final InvoiceItemModelDao invoiceItemModelDao = invoiceItemSqlDao.getById(creditId.toString(), context); if (invoiceItemModelDao == null) { throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, creditId.toString()); } return invoiceItemModelDao; } });
@Override public Response toResponse(final InvoiceApiException exception) { if (exception.getCode() == ErrorCode.INVOICE_ACCOUNT_ID_INVALID.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_INVALID_DATE_SEQUENCE.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_INVALID_TRANSITION.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID.getCode()) { return buildNotFoundResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_NO_SUCH_CREDIT.getCode()) { return buildNotFoundResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_NOT_FOUND.getCode()) { return buildNotFoundResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_PAYMENT_BY_ATTEMPT_NOT_FOUND.getCode()) { return buildNotFoundResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_PAYMENT_NOT_FOUND.getCode()) { return buildNotFoundResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_TARGET_DATE_TOO_FAR_IN_THE_FUTURE.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.CREDIT_AMOUNT_INVALID.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE.getCode()) { return buildBadRequestResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_ITEM_NOT_FOUND.getCode()) { return buildNotFoundResponse(exception, uriInfo); } else if (exception.getCode() == ErrorCode.INVOICE_NO_SUCH_EXTERNAL_CHARGE.getCode()) { return buildBadRequestResponse(exception, uriInfo);
public BigDecimal computePositiveRefundAmount(final InvoicePaymentModelDao payment, final BigDecimal requestedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) throws InvoiceApiException { final BigDecimal maxRefundAmount = payment.getAmount() == null ? BigDecimal.ZERO : payment.getAmount(); final BigDecimal requestedPositiveAmount = requestedRefundAmount == null ? maxRefundAmount : requestedRefundAmount; // This check is good but not enough, we need to also take into account previous refunds // (But that should have been checked in the payment call already) if (requestedPositiveAmount.compareTo(maxRefundAmount) > 0) { throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_TOO_HIGH, requestedPositiveAmount, maxRefundAmount); } // Verify if the requested amount matches the invoice items to adjust, if specified BigDecimal amountFromItems = BigDecimal.ZERO; for (final BigDecimal itemAmount : invoiceItemIdsWithAmounts.values()) { amountFromItems = amountFromItems.add(itemAmount); } // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && requestedPositiveAmount.compareTo(amountFromItems) < 0) { throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_DONT_MATCH_ITEMS_TO_ADJUST, requestedPositiveAmount, amountFromItems); } return requestedPositiveAmount; }
@Test(groups = "slow") public void testCantAdjustInvoiceWithNegativeAmount() throws Exception { try { invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, BigDecimal.TEN.negate(), clock.getUTCToday(), accountCurrency, null, null, null, callContext); Assert.fail("Should not have been able to adjust an invoice with a negative amount"); } catch (InvoiceApiException e) { Assert.assertEquals(e.getCode(), ErrorCode.CREDIT_AMOUNT_INVALID.getCode()); } }